mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[APM] Serverless Onboarding with Custom Tutorials (#158228)
Closes https://github.com/elastic/kibana/issues/155371 ## Summary PR adds Serverless Onboarding flow using Custom Integration. This would also lay the foundation for us to complete get rid of Home Tutorial App and move the remaining `onPrem` and `cloud` tutorials which are currently still loaded using Home Tutorial App. 1. Adds new Custom Integration for Serverless Onboarding (Toggling Home AApp Tutorial Integration) 2. Since we are migrating away from the Home App Tutorials, lot of existing code has been duplicated and refactored for the custom implementation. Home App Tutorial would require the Server to register all the steps and the client to only register a custom component which then would be loaded by Home App Tutorial component. We don't need to follow this approach any more. All the UX logic has now been moved to the Public folder with only Custom Integration done on the `server/plugin.ts`. 3. As we are not sure how the solutions will be informed about being running on Serverless or not, I have introduced a new variable in `serverless.oblt.yml` file called `xpack.apm.serverlessOnboarding: true`. With this the development has been done. This can be changed to actual logic once we know more. 4. A new configuration `xpack.apm.managedServiceUrl` for accessing Managed Service URL is also being added by Control Plane team as part of https://elasticco.atlassian.net/browse/CP-2403. Hence this PR expects this property to be present for Serverless. 5. Unit tests to toggle between `secret_token` and `api_key` depending on availability has been added. No API Tests were added as no new API created. Cypress Tests cannot be added due to Serverless ## Need help reviewing the PR ? 1. `config/serverless.oblt.yml` - Adds the new flag which would enable this flow 2. `x-pack/plugins/apm/common/tutorial/tutorials.ts` - Defines the configuration required to register the APM's Tutorial Custom Integration 3. `x-pack/plugins/apm/public/components/app/tutorials/commands` - This directory contains all the agent specific data required to load the TABLE with settings required for configuring APM MIS. 4. `x-pack/plugins/apm/public/components/app/tutorials/instructions` - This folder contains all the individual agent specific instructions in the format used by [EuiSteps](https://eui.elastic.co/#/navigation/steps#complex-steps) 5. `x-pack/plugins/apm/public/components/routing` - Here we register our custom route 6. Changes on the server side a quite small and they only register the custom integration. 7. `x-pack/plugins/apm/public/components/app/tutorials/serverless_instructions.tsx` - This file currently defines all the logic for registering Serverless instructions. We will soon have similar files for `onPrem` and `cloud` instructions ### Risk Matrix | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | The flow depends on presence of a flag in `kibana.yml` file. | Low | High | By default this flow will be disabled and would fallback to traditional onboarding in absence of the flag. | ### Demod60f0610
-1fea-4540-86f5-2d72ab97f640 ### Updated Demo with Create API Button inside the tablee84d8d6c
-a048-4638-9b63-45080feca90b --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
177914bcaf
commit
ac2fc4c3be
50 changed files with 3637 additions and 34 deletions
|
@ -19,3 +19,11 @@ xpack.serverless.plugin.developer.projectSwitcher.currentType: 'observability'
|
|||
|
||||
## Disable adding the component template `.fleet_agent_id_verification-1` to every index template for each datastream for each integration
|
||||
xpack.fleet.agentIdVerificationEnabled: false
|
||||
|
||||
## APM Serverless Onboarding flow
|
||||
xpack.apm.serverlessOnboarding: true
|
||||
|
||||
## Required for force installation of APM Package
|
||||
xpack.fleet.packages:
|
||||
- name: apm
|
||||
version: latest
|
||||
|
|
|
@ -177,6 +177,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.apm.serviceMapEnabled (boolean)',
|
||||
'xpack.apm.ui.enabled (boolean)',
|
||||
'xpack.apm.ui.maxTraceItems (number)',
|
||||
'xpack.apm.managedServiceUrl (any)',
|
||||
'xpack.apm.serverlessOnboarding (any)',
|
||||
'xpack.apm.latestAgentVersionsUrl (string)',
|
||||
'xpack.cases.files.allowedMimeTypes (array)',
|
||||
'xpack.cases.files.maxSize (number)',
|
||||
|
|
32
x-pack/plugins/apm/common/tutorial/tutorials.ts
Normal file
32
x-pack/plugins/apm/common/tutorial/tutorials.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CustomIntegration } from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
const APM_INTEGRATION_CATEGORIES = ['observability', 'apm'];
|
||||
|
||||
export const apmTutorialCustomIntegration: Omit<CustomIntegration, 'type'> = {
|
||||
id: 'apm',
|
||||
title: i18n.translate('xpack.apm.tutorial.specProvider.name', {
|
||||
defaultMessage: 'APM',
|
||||
}),
|
||||
categories: APM_INTEGRATION_CATEGORIES,
|
||||
uiInternalPath: '/app/apm/onboarding',
|
||||
description: i18n.translate('xpack.apm.tutorial.introduction', {
|
||||
defaultMessage:
|
||||
'Collect performance metrics from your applications with Elastic APM.',
|
||||
}),
|
||||
icons: [
|
||||
{
|
||||
type: 'eui',
|
||||
src: 'apmApp',
|
||||
},
|
||||
],
|
||||
shipper: 'tutorial',
|
||||
isBeta: false,
|
||||
};
|
|
@ -45,6 +45,7 @@
|
|||
"spaces",
|
||||
"taskManager",
|
||||
"usageCollection",
|
||||
"customIntegrations", // Move this to requiredPlugins after completely migrating from the Tutorials Home App
|
||||
"licenseManagement"
|
||||
],
|
||||
"requiredBundles": [
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiCodeBlock, EuiSpacer } from '@elastic/eui';
|
||||
import {
|
||||
getApmAgentCommands,
|
||||
getApmAgentVariables,
|
||||
getApmAgentLineNumbers,
|
||||
getApmAgentHighlightLang,
|
||||
} from './commands/get_apm_agent_commands';
|
||||
import { AgentConfigurationTable } from './agent_config_table';
|
||||
|
||||
export function AgentConfigInstructions({
|
||||
variantId,
|
||||
apmServerUrl,
|
||||
secretToken,
|
||||
apiKey,
|
||||
createApiKey,
|
||||
createApiKeyLoading,
|
||||
}: {
|
||||
variantId: string;
|
||||
apmServerUrl: string;
|
||||
secretToken?: string;
|
||||
apiKey?: string | null;
|
||||
createApiKey?: () => void;
|
||||
createApiKeyLoading?: boolean;
|
||||
}) {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId,
|
||||
apmServerUrl,
|
||||
secretToken,
|
||||
apiKey,
|
||||
});
|
||||
|
||||
const variables = getApmAgentVariables(variantId, secretToken);
|
||||
const lineNumbers = getApmAgentLineNumbers(variantId, apiKey);
|
||||
const highlightLang = getApmAgentHighlightLang(variantId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<AgentConfigurationTable
|
||||
variables={variables}
|
||||
data={{ apmServerUrl, secretToken, apiKey }}
|
||||
createApiKey={createApiKey}
|
||||
createApiKeyLoading={createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
|
||||
<EuiCodeBlock
|
||||
isCopyable
|
||||
language={highlightLang || 'bash'}
|
||||
data-test-subj="commands"
|
||||
lineNumbers={lineNumbers}
|
||||
whiteSpace="pre"
|
||||
>
|
||||
{commands}
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { ValuesType } from 'utility-types';
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiText,
|
||||
EuiBasicTableColumn,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
function ConfigurationValueColumn({
|
||||
columnKey,
|
||||
value,
|
||||
createApiKey,
|
||||
createApiKeyLoading,
|
||||
}: {
|
||||
columnKey: string;
|
||||
value: string | null;
|
||||
createApiKey?: () => void;
|
||||
createApiKeyLoading?: boolean;
|
||||
}) {
|
||||
const shouldRenderCreateApiKeyButton =
|
||||
columnKey === 'apiKey' && value === null;
|
||||
|
||||
if (shouldRenderCreateApiKeyButton) {
|
||||
return (
|
||||
<EuiButton
|
||||
data-test-subj="createApiKeyAndId"
|
||||
fill
|
||||
onClick={createApiKey}
|
||||
isLoading={createApiKeyLoading}
|
||||
>
|
||||
{i18n.translate('xpack.apm.tutorial.apiKey.create', {
|
||||
defaultMessage: 'Create API Key',
|
||||
})}
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiText size="s" color="accent">
|
||||
{value}
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
||||
export function AgentConfigurationTable({
|
||||
variables,
|
||||
data,
|
||||
createApiKey,
|
||||
createApiKeyLoading,
|
||||
}: {
|
||||
variables: { [key: string]: string };
|
||||
data: {
|
||||
apmServerUrl?: string;
|
||||
secretToken?: string;
|
||||
apiKey?: string | null;
|
||||
};
|
||||
createApiKey?: () => void;
|
||||
createApiKeyLoading?: boolean;
|
||||
}) {
|
||||
if (!variables) return null;
|
||||
|
||||
const defaultValues = {
|
||||
apmServiceName: 'my-service-name',
|
||||
apmEnvironment: 'my-environment',
|
||||
};
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<ValuesType<typeof items>>> = [
|
||||
{
|
||||
field: 'setting',
|
||||
name: i18n.translate('xpack.apm.onboarding.agent.column.configSettings', {
|
||||
defaultMessage: 'Configuration setting',
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
name: i18n.translate('xpack.apm.onboarding.agent.column.configValue', {
|
||||
defaultMessage: 'Configuration value',
|
||||
}),
|
||||
render: (_, { value, key }) => (
|
||||
<ConfigurationValueColumn
|
||||
columnKey={key}
|
||||
value={value}
|
||||
createApiKey={createApiKey}
|
||||
createApiKeyLoading={createApiKeyLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const items = Object.entries(variables).map(([key, value]) => ({
|
||||
setting: value,
|
||||
value: get({ ...data, ...defaultValues }, key),
|
||||
key,
|
||||
}));
|
||||
return <EuiBasicTable items={items} columns={columns} />;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const djangoVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'SERVICE_NAME',
|
||||
...(secretToken && { secretToken: 'SECRET_TOKEN' }),
|
||||
...(!secretToken && { apiKey: 'API_KEY' }),
|
||||
apmServerUrl: 'SERVER_URL',
|
||||
apmEnvironment: 'ENVIRONMENT',
|
||||
});
|
||||
|
||||
export const djangoHighlightLang = 'py';
|
||||
|
||||
export const djangoLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '1, 3, 5, 7, 9, 12, 15, 18-19, 21, 23, 25',
|
||||
});
|
||||
|
||||
export const django = `INSTALLED_APPS = (
|
||||
# ${i18n.translate(
|
||||
'xpack.apm.onboarding.djangoClient.configure.commands.addAgentComment',
|
||||
{
|
||||
defaultMessage: 'Add the agent to installed apps',
|
||||
}
|
||||
)}
|
||||
'elasticapm.contrib.django',
|
||||
# ...
|
||||
)
|
||||
|
||||
ELASTIC_APM = {
|
||||
# {{serviceNameHint}}
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
'API_KEY': '{{{apiKey}}}',
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
'SECRET_TOKEN': '{{{secretToken}}}',
|
||||
{{/secretToken}}
|
||||
|
||||
# {{{serverUrlHint}}}
|
||||
'SERVER_URL': '{{{apmServerUrl}}}',
|
||||
|
||||
# {{{serviceEnvironmentHint}}}
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
MIDDLEWARE = (
|
||||
# ${i18n.translate(
|
||||
'xpack.apm.onboarding.djangoClient.configure.commands.addTracingMiddlewareComment',
|
||||
{
|
||||
defaultMessage: 'Add our tracing middleware to send performance metrics',
|
||||
}
|
||||
)}
|
||||
'elasticapm.contrib.django.middleware.TracingMiddleware',
|
||||
#...
|
||||
)`;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const dotnetVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'ServiceName',
|
||||
...(secretToken && { secretToken: 'SecretToken' }),
|
||||
...(!secretToken && { apiKey: 'ApiKey' }),
|
||||
apmServerUrl: 'ServerUrl',
|
||||
apmEnvironment: 'Environment',
|
||||
});
|
||||
|
||||
export const dotnetHighlightLang = 'dotnet';
|
||||
|
||||
export const dotnetLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '1-2, 4, 6, 8, 10-12',
|
||||
});
|
||||
|
||||
export const dotnet = `{
|
||||
"ElasticApm": {
|
||||
/// {{serviceNameHint}} ${i18n.translate(
|
||||
'xpack.apm.onboarding.dotnetClient.createConfig.commands.defaultServiceName',
|
||||
{
|
||||
defaultMessage: 'Default is the entry assembly of the application.',
|
||||
}
|
||||
)}
|
||||
"ServiceName": "my-service-name",
|
||||
{{^secretToken}}
|
||||
/// {{apiKeyHint}}
|
||||
"ApiKey": "{{{apiKey}}}",
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
/// {{secretTokenHint}}
|
||||
"SecretToken": "{{{secretToken}}}",
|
||||
{{/secretToken}}
|
||||
/// {{{serverUrlHint}}}
|
||||
"ServerUrl": "{{{apmServerUrl}}}",
|
||||
/// {{{serviceEnvironmentHint}}}
|
||||
"Environment": "my-environment",
|
||||
}
|
||||
}`;
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const flaskVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'SERVICE_NAME',
|
||||
...(secretToken && { secretToken: 'SECRET_TOKEN' }),
|
||||
...(!secretToken && { apiKey: 'API_KEY' }),
|
||||
apmServerUrl: 'SERVER_URL',
|
||||
apmEnvironment: 'ENVIRONMENT',
|
||||
});
|
||||
|
||||
export const flaskHighlightLang = 'py';
|
||||
|
||||
export const flaskLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '2-4, 7-8, 10, 13, 16, 19-22',
|
||||
});
|
||||
|
||||
export const flask = `# ${i18n.translate(
|
||||
'xpack.apm.onboarding.flaskClient.configure.commands.initializeUsingEnvironmentVariablesComment',
|
||||
{
|
||||
defaultMessage: 'Initialize using environment variables',
|
||||
}
|
||||
)}
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app = Flask(__name__)
|
||||
apm = ElasticAPM(app)
|
||||
|
||||
# ${i18n.translate(
|
||||
'xpack.apm.onboarding.flaskClient.configure.commands.configureElasticApmComment',
|
||||
{
|
||||
defaultMessage: "Or use ELASTIC_APM in your application's settings",
|
||||
}
|
||||
)}
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app.config['ELASTIC_APM'] = {
|
||||
# {{serviceNameHint}}
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
'API_KEY': '{{{apiKey}}}',
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
'SECRET_TOKEN': '{{{secretToken}}}',
|
||||
{{/secretToken}}
|
||||
|
||||
# {{{serverUrlHint}}}
|
||||
'SERVER_URL': '{{{apmServerUrl}}}',
|
||||
|
||||
{{{serviceEnvironmentHint}}}
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
apm = ElasticAPM(app)`;
|
|
@ -0,0 +1,696 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getApmAgentCommands } from './get_apm_agent_commands';
|
||||
|
||||
describe('getCommands', () => {
|
||||
describe('Unknown agent', () => {
|
||||
it('renders empty command', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'foo',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).toBe('');
|
||||
});
|
||||
});
|
||||
describe('Java agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'java',
|
||||
});
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\\\
|
||||
-Delastic.apm.service_name=my-service-name \\\\
|
||||
-Delastic.apm.api_key= \\\\
|
||||
-Delastic.apm.server_url= \\\\
|
||||
-Delastic.apm.environment=my-environment \\\\
|
||||
-Delastic.apm.application_packages=org.example \\\\
|
||||
-jar my-service-name.jar"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'java',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\\\
|
||||
-Delastic.apm.service_name=my-service-name \\\\
|
||||
-Delastic.apm.secret_token=foobar \\\\
|
||||
-Delastic.apm.server_url=localhost:8220 \\\\
|
||||
-Delastic.apm.environment=my-environment \\\\
|
||||
-Delastic.apm.application_packages=org.example \\\\
|
||||
-jar my-service-name.jar"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'java',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\\\
|
||||
-Delastic.apm.service_name=my-service-name \\\\
|
||||
-Delastic.apm.secret_token=foobar \\\\
|
||||
-Delastic.apm.server_url=localhost:8220 \\\\
|
||||
-Delastic.apm.environment=my-environment \\\\
|
||||
-Delastic.apm.application_packages=org.example \\\\
|
||||
-jar my-service-name.jar"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Node.js agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'node',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"// Add this to the very top of the first file loaded in your app
|
||||
var apm = require('elastic-apm-node').start({
|
||||
|
||||
// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Overrides the service name in package.json.
|
||||
serviceName: 'my-service-name',
|
||||
|
||||
// Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
apiKey: '',
|
||||
|
||||
// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
serverUrl: '',
|
||||
|
||||
// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'
|
||||
})"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'node',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"// Add this to the very top of the first file loaded in your app
|
||||
var apm = require('elastic-apm-node').start({
|
||||
|
||||
// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Overrides the service name in package.json.
|
||||
serviceName: 'my-service-name',
|
||||
|
||||
// Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secretToken: 'foobar',
|
||||
|
||||
// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
serverUrl: 'localhost:8220',
|
||||
|
||||
// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'
|
||||
})"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'node',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"// Add this to the very top of the first file loaded in your app
|
||||
var apm = require('elastic-apm-node').start({
|
||||
|
||||
// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Overrides the service name in package.json.
|
||||
serviceName: 'my-service-name',
|
||||
|
||||
// Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secretToken: 'foobar',
|
||||
|
||||
// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
serverUrl: 'localhost:8220',
|
||||
|
||||
// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'
|
||||
})"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('Django agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'django',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"INSTALLED_APPS = (
|
||||
# Add the agent to installed apps
|
||||
'elasticapm.contrib.django',
|
||||
# ...
|
||||
)
|
||||
|
||||
ELASTIC_APM = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
'API_KEY': '',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': '',
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
MIDDLEWARE = (
|
||||
# Add our tracing middleware to send performance metrics
|
||||
'elasticapm.contrib.django.middleware.TracingMiddleware',
|
||||
#...
|
||||
)"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'django',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"INSTALLED_APPS = (
|
||||
# Add the agent to installed apps
|
||||
'elasticapm.contrib.django',
|
||||
# ...
|
||||
)
|
||||
|
||||
ELASTIC_APM = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
'SECRET_TOKEN': 'foobar',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': 'localhost:8220',
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
MIDDLEWARE = (
|
||||
# Add our tracing middleware to send performance metrics
|
||||
'elasticapm.contrib.django.middleware.TracingMiddleware',
|
||||
#...
|
||||
)"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'django',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"INSTALLED_APPS = (
|
||||
# Add the agent to installed apps
|
||||
'elasticapm.contrib.django',
|
||||
# ...
|
||||
)
|
||||
|
||||
ELASTIC_APM = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
'SECRET_TOKEN': 'foobar',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': 'localhost:8220',
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
MIDDLEWARE = (
|
||||
# Add our tracing middleware to send performance metrics
|
||||
'elasticapm.contrib.django.middleware.TracingMiddleware',
|
||||
#...
|
||||
)"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('Flask agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'flask',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app = Flask(__name__)
|
||||
apm = ElasticAPM(app)
|
||||
|
||||
# Or use ELASTIC_APM in your application's settings
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app.config['ELASTIC_APM'] = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
'API_KEY': '',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': '',
|
||||
|
||||
The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
apm = ElasticAPM(app)"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'flask',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app = Flask(__name__)
|
||||
apm = ElasticAPM(app)
|
||||
|
||||
# Or use ELASTIC_APM in your application's settings
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app.config['ELASTIC_APM'] = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
'SECRET_TOKEN': 'foobar',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': 'localhost:8220',
|
||||
|
||||
The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
apm = ElasticAPM(app)"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'flask',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app = Flask(__name__)
|
||||
apm = ElasticAPM(app)
|
||||
|
||||
# Or use ELASTIC_APM in your application's settings
|
||||
from elasticapm.contrib.flask import ElasticAPM
|
||||
app.config['ELASTIC_APM'] = {
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
'SERVICE_NAME': 'my-service-name',
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
'SECRET_TOKEN': 'foobar',
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
'SERVER_URL': 'localhost:8220',
|
||||
|
||||
The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
'ENVIRONMENT': 'my-environment',
|
||||
}
|
||||
|
||||
apm = ElasticAPM(app)"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('Ruby on Rails agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rails',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rails app.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
api_key: ''
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: ''
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rails',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rails app.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secret_token: 'foobar'
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: 'localhost:8220'
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rails',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rails app.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secret_token: 'foobar'
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: 'localhost:8220'
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('Rack agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rack',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rack app's class.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
api_key: ''
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: ''
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rack',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rack app's class.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secret_token: 'foobar'
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: 'localhost:8220'
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'rack',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# config/elastic_apm.yml:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Defaults to the name of your Rack app's class.
|
||||
service_name: 'my-service-name'
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
secret_token: 'foobar'
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
server_url: 'localhost:8220'
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
environment: 'my-environment'"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('Go agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'go',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. If not specified, the executable name will be used.
|
||||
export ELASTIC_APM_SERVICE_NAME=my-service-name
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
export ELASTIC_APM_API_KEY=
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
export ELASTIC_APM_SERVER_URL=
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
export ELASTIC_APM_ENVIRONMENT=my-environment
|
||||
"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'go',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. If not specified, the executable name will be used.
|
||||
export ELASTIC_APM_SERVICE_NAME=my-service-name
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
export ELASTIC_APM_SECRET_TOKEN=foobar
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
export ELASTIC_APM_SERVER_URL=localhost:8220
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
export ELASTIC_APM_ENVIRONMENT=my-environment
|
||||
"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'go',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# Initialize using environment variables:
|
||||
|
||||
# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. If not specified, the executable name will be used.
|
||||
export ELASTIC_APM_SERVICE_NAME=my-service-name
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
export ELASTIC_APM_SECRET_TOKEN=foobar
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
export ELASTIC_APM_SERVER_URL=localhost:8220
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
export ELASTIC_APM_ENVIRONMENT=my-environment
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('DotNet agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'dotnet',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"{
|
||||
\\"ElasticApm\\": {
|
||||
/// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application.
|
||||
\\"ServiceName\\": \\"my-service-name\\",
|
||||
/// Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
\\"ApiKey\\": \\"\\",
|
||||
/// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
\\"ServerUrl\\": \\"\\",
|
||||
/// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
\\"Environment\\": \\"my-environment\\",
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'dotnet',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"{
|
||||
\\"ElasticApm\\": {
|
||||
/// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application.
|
||||
\\"ServiceName\\": \\"my-service-name\\",
|
||||
/// Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
\\"SecretToken\\": \\"foobar\\",
|
||||
/// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
\\"ServerUrl\\": \\"localhost:8220\\",
|
||||
/// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
\\"Environment\\": \\"my-environment\\",
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'dotnet',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"{
|
||||
\\"ElasticApm\\": {
|
||||
/// The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application.
|
||||
\\"ServiceName\\": \\"my-service-name\\",
|
||||
/// Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
\\"SecretToken\\": \\"foobar\\",
|
||||
/// Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
\\"ServerUrl\\": \\"localhost:8220\\",
|
||||
/// The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
\\"Environment\\": \\"my-environment\\",
|
||||
}
|
||||
}"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('PHP agent', () => {
|
||||
it('renders empty commands', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'php',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
elastic_apm.service_name=\\"my-service-name\\"
|
||||
|
||||
# Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.
|
||||
elastic_apm.api_key=\\"\\"
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
elastic_apm.server_url=\\"\\"
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
elastic_apm.environment=\\"my-environment\\""
|
||||
`);
|
||||
});
|
||||
it('renders with secret token and url', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'php',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
elastic_apm.service_name=\\"my-service-name\\"
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
elastic_apm.secret_token=\\"foobar\\"
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
elastic_apm.server_url=\\"localhost:8220\\"
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
elastic_apm.environment=\\"my-environment\\""
|
||||
`);
|
||||
});
|
||||
it('renders with api key even though secret token is present', () => {
|
||||
const commands = getApmAgentCommands({
|
||||
variantId: 'php',
|
||||
apmServerUrl: 'localhost:8220',
|
||||
secretToken: 'foobar',
|
||||
apiKey: 'myApiKey',
|
||||
});
|
||||
expect(commands).not.toBe('');
|
||||
expect(commands).toMatchInlineSnapshot(`
|
||||
"# The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.
|
||||
elastic_apm.service_name=\\"my-service-name\\"
|
||||
|
||||
# Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.
|
||||
elastic_apm.secret_token=\\"foobar\\"
|
||||
|
||||
# Set the custom APM Server URL (default: http://localhost:8200). The URL must be fully qualified, including protocol (http or https) and port.
|
||||
elastic_apm.server_url=\\"localhost:8220\\"
|
||||
|
||||
# The name of the environment this service is deployed in, e.g., \\"production\\" or \\"staging\\". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.
|
||||
elastic_apm.environment=\\"my-environment\\""
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import Mustache from 'mustache';
|
||||
import {
|
||||
java,
|
||||
javaVariables,
|
||||
javaLineNumbers,
|
||||
javaHighlightLang,
|
||||
} from './java';
|
||||
import {
|
||||
node,
|
||||
nodeVariables,
|
||||
nodeLineNumbers,
|
||||
nodeHighlightLang,
|
||||
} from './node';
|
||||
import {
|
||||
django,
|
||||
djangoVariables,
|
||||
djangoLineNumbers,
|
||||
djangoHighlightLang,
|
||||
} from './django';
|
||||
import {
|
||||
flask,
|
||||
flaskVariables,
|
||||
flaskLineNumbers,
|
||||
flaskHighlightLang,
|
||||
} from './flask';
|
||||
import {
|
||||
rails,
|
||||
railsVariables,
|
||||
railsLineNumbers,
|
||||
railsHighlightLang,
|
||||
} from './rails';
|
||||
import {
|
||||
rack,
|
||||
rackVariables,
|
||||
rackLineNumbers,
|
||||
rackHighlightLang,
|
||||
} from './rack';
|
||||
import { go, goVariables, goLineNumbers, goHighlightLang } from './go';
|
||||
import {
|
||||
dotnet,
|
||||
dotnetVariables,
|
||||
dotnetLineNumbers,
|
||||
dotnetHighlightLang,
|
||||
} from './dotnet';
|
||||
import { php, phpVariables, phpLineNumbers, phpHighlightLang } from './php';
|
||||
import {
|
||||
serviceNameHint,
|
||||
serviceEnvironmentHint,
|
||||
serverUrlHint,
|
||||
secretTokenHint,
|
||||
apiKeyHint,
|
||||
} from './shared_hints';
|
||||
|
||||
const apmAgentCommandsMap: Record<string, string> = {
|
||||
java,
|
||||
node,
|
||||
django,
|
||||
flask,
|
||||
rails,
|
||||
rack,
|
||||
go,
|
||||
dotnet,
|
||||
php,
|
||||
};
|
||||
|
||||
interface Variables {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const apmAgentVariablesMap: (
|
||||
secretToken?: string
|
||||
) => Record<string, Variables> = (secretToken?: string) => ({
|
||||
java: javaVariables(secretToken),
|
||||
node: nodeVariables(secretToken),
|
||||
django: djangoVariables(secretToken),
|
||||
flask: flaskVariables(secretToken),
|
||||
rails: railsVariables(secretToken),
|
||||
rack: rackVariables(secretToken),
|
||||
go: goVariables(secretToken),
|
||||
dotnet: dotnetVariables(secretToken),
|
||||
php: phpVariables(secretToken),
|
||||
});
|
||||
|
||||
interface LineNumbers {
|
||||
[key: string]: string | number | object;
|
||||
}
|
||||
|
||||
const apmAgentLineNumbersMap: (
|
||||
apiKey?: string | null
|
||||
) => Record<string, LineNumbers> = (apiKey?: string | null) => ({
|
||||
java: javaLineNumbers(apiKey),
|
||||
node: nodeLineNumbers(),
|
||||
django: djangoLineNumbers(),
|
||||
flask: flaskLineNumbers(),
|
||||
rails: railsLineNumbers(),
|
||||
rack: rackLineNumbers(),
|
||||
go: goLineNumbers(),
|
||||
dotnet: dotnetLineNumbers(),
|
||||
php: phpLineNumbers(),
|
||||
});
|
||||
|
||||
const apmAgentHighlightLangMap: Record<string, string> = {
|
||||
java: javaHighlightLang,
|
||||
node: nodeHighlightLang,
|
||||
django: djangoHighlightLang,
|
||||
flask: flaskHighlightLang,
|
||||
rails: railsHighlightLang,
|
||||
rack: rackHighlightLang,
|
||||
go: goHighlightLang,
|
||||
dotnet: dotnetHighlightLang,
|
||||
php: phpHighlightLang,
|
||||
};
|
||||
|
||||
export function getApmAgentCommands({
|
||||
variantId,
|
||||
apmServerUrl,
|
||||
secretToken,
|
||||
apiKey,
|
||||
}: {
|
||||
variantId: string;
|
||||
apmServerUrl?: string;
|
||||
secretToken?: string;
|
||||
apiKey?: string | null;
|
||||
}) {
|
||||
const commands = apmAgentCommandsMap[variantId];
|
||||
if (!commands) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Mustache.render(commands, {
|
||||
apmServerUrl,
|
||||
secretToken,
|
||||
apiKey,
|
||||
serviceNameHint,
|
||||
serviceEnvironmentHint,
|
||||
serverUrlHint,
|
||||
secretTokenHint,
|
||||
apiKeyHint,
|
||||
});
|
||||
}
|
||||
|
||||
export function getApmAgentVariables(variantId: string, secretToken?: string) {
|
||||
return apmAgentVariablesMap(secretToken)[variantId];
|
||||
}
|
||||
|
||||
export function getApmAgentLineNumbers(
|
||||
variantId: string,
|
||||
apiKey?: string | null
|
||||
) {
|
||||
return apmAgentLineNumbersMap(apiKey)[variantId];
|
||||
}
|
||||
|
||||
export function getApmAgentHighlightLang(variantId: string) {
|
||||
return apmAgentHighlightLangMap[variantId];
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const goVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'ELASTIC_APM_SERVICE_NAME',
|
||||
...(secretToken && { secretToken: 'ELASTIC_APM_SECRET_TOKEN' }),
|
||||
...(!secretToken && { apiKey: 'ELASTIC_APM_API_KEY' }),
|
||||
apmServerUrl: 'ELASTIC_APM_SERVER_URL',
|
||||
apmEnvironment: 'ELASTIC_APM_ENVIRONMENT',
|
||||
});
|
||||
|
||||
export const goHighlightLang = 'go';
|
||||
|
||||
export const goLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '4, 7, 10, 13',
|
||||
});
|
||||
|
||||
export const go = `# ${i18n.translate(
|
||||
'xpack.apm.onboarding.goClient.configure.commands.initializeUsingEnvironmentVariablesComment',
|
||||
{
|
||||
defaultMessage: 'Initialize using environment variables:',
|
||||
}
|
||||
)}
|
||||
|
||||
# {{serviceNameHint}} ${i18n.translate(
|
||||
'xpack.apm.onboarding.goClient.configure.commands.usedExecutableNameComment',
|
||||
{
|
||||
defaultMessage: 'If not specified, the executable name will be used.',
|
||||
}
|
||||
)}
|
||||
export ELASTIC_APM_SERVICE_NAME=my-service-name
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
export ELASTIC_APM_API_KEY={{{apiKey}}}
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
export ELASTIC_APM_SECRET_TOKEN={{{secretToken}}}
|
||||
{{/secretToken}}
|
||||
|
||||
# {{{serverUrlHint}}}
|
||||
export ELASTIC_APM_SERVER_URL={{{apmServerUrl}}}
|
||||
|
||||
# {{{serviceEnvironmentHint}}}
|
||||
export ELASTIC_APM_ENVIRONMENT=my-environment
|
||||
`;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
serviceNameHint,
|
||||
secretTokenHint,
|
||||
serverUrlHint,
|
||||
serviceEnvironmentHint,
|
||||
apiKeyHint,
|
||||
} from './shared_hints';
|
||||
|
||||
export const javaVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'Delastic.apm.service_name',
|
||||
...(secretToken && { secretToken: 'Delastic.apm.secret_token' }),
|
||||
...(!secretToken && { apiKey: 'Delastic.apm.api_key' }),
|
||||
apmServerUrl: 'Delastic.apm.server_url',
|
||||
apmEnvironment: 'Delastic.apm.environment',
|
||||
});
|
||||
|
||||
export const javaHighlightLang = 'java';
|
||||
|
||||
export const javaLineNumbers = (apiKey?: string | null) => ({
|
||||
start: 1,
|
||||
highlight: '',
|
||||
annotations: {
|
||||
2: serviceNameHint,
|
||||
3: apiKey ? apiKeyHint : secretTokenHint,
|
||||
4: serverUrlHint,
|
||||
5: serviceEnvironmentHint,
|
||||
},
|
||||
});
|
||||
export const java = `java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\
|
||||
-Delastic.apm.service_name=my-service-name \\
|
||||
{{^secretToken}}
|
||||
-Delastic.apm.api_key={{{apiKey}}} \\
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
-Delastic.apm.secret_token={{{secretToken}}} \\
|
||||
{{/secretToken}}
|
||||
-Delastic.apm.server_url={{{apmServerUrl}}} \\
|
||||
-Delastic.apm.environment=my-environment \\
|
||||
-Delastic.apm.application_packages=org.example \\
|
||||
-jar my-service-name.jar`;
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const nodeVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'serviceName',
|
||||
...(secretToken && { secretToken: 'secretToken' }),
|
||||
...(!secretToken && { apiKey: 'apiKey' }),
|
||||
apmServerUrl: 'serverUrl',
|
||||
apmEnvironment: 'environment',
|
||||
});
|
||||
|
||||
export const nodeHighlightLang = 'js';
|
||||
|
||||
export const nodeLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '2, 5, 8, 11, 14-15',
|
||||
});
|
||||
|
||||
export const node = `// ${i18n.translate(
|
||||
'xpack.apm.onboarding.nodeClient.configure.commands.addThisToTheFileTopComment',
|
||||
{
|
||||
defaultMessage:
|
||||
'Add this to the very top of the first file loaded in your app',
|
||||
}
|
||||
)}
|
||||
var apm = require('elastic-apm-node').start({
|
||||
|
||||
// {{serviceNameHint}} ${i18n.translate(
|
||||
'xpack.apm.onboarding.nodeClient.createConfig.commands.serviceName',
|
||||
{
|
||||
defaultMessage: 'Overrides the service name in package.json.',
|
||||
}
|
||||
)}
|
||||
serviceName: 'my-service-name',
|
||||
|
||||
{{^secretToken}}
|
||||
// {{apiKeyHint}}
|
||||
apiKey: '{{{apiKey}}}',
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
// {{secretTokenHint}}
|
||||
secretToken: '{{{secretToken}}}',
|
||||
{{/secretToken}}
|
||||
|
||||
// {{{serverUrlHint}}}
|
||||
serverUrl: '{{{apmServerUrl}}}',
|
||||
|
||||
// {{{serviceEnvironmentHint}}}
|
||||
environment: 'my-environment'
|
||||
})`;
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
export const phpVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'elastic_apm.service_name',
|
||||
...(secretToken && { secretToken: 'elastic_apm.secret_token' }),
|
||||
...(!secretToken && { apiKey: 'elastic_apm.api_key' }),
|
||||
apmServerUrl: 'elastic_apm.server_url',
|
||||
apmEnvironment: 'elastic_apm.environment',
|
||||
});
|
||||
|
||||
export const phpHighlightLang = 'php';
|
||||
|
||||
export const phpLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '2, 5, 8, 11',
|
||||
});
|
||||
|
||||
export const php = `# {{serviceNameHint}}
|
||||
elastic_apm.service_name="my-service-name"
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
elastic_apm.api_key="{{{apiKey}}}"
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
elastic_apm.secret_token="{{{secretToken}}}"
|
||||
{{/secretToken}}
|
||||
|
||||
# {{serverUrlHint}}
|
||||
elastic_apm.server_url="{{{apmServerUrl}}}"
|
||||
|
||||
# {{{serviceEnvironmentHint}}}
|
||||
elastic_apm.environment="my-environment"`;
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const rackVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'service_name',
|
||||
...(secretToken && { secretToken: 'secret_token' }),
|
||||
...(!secretToken && { apiKey: 'api_key' }),
|
||||
apmServerUrl: 'server_url',
|
||||
apmEnvironment: 'environment',
|
||||
});
|
||||
|
||||
export const rackHighlightLang = 'rb';
|
||||
|
||||
export const rackLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '4, 7, 10, 13',
|
||||
});
|
||||
|
||||
export const rack = `# config/elastic_apm.yml:
|
||||
|
||||
# {{serviceNameHint}} ${i18n.translate(
|
||||
'xpack.apm.onboarding.rackClient.createConfig.commands.defaultsToTheNameOfRackAppClassComment',
|
||||
{
|
||||
defaultMessage: "Defaults to the name of your Rack app's class.",
|
||||
}
|
||||
)}
|
||||
service_name: 'my-service-name'
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
api_key: '{{{apiKey}}}'
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
secret_token: '{{{secretToken}}}'
|
||||
{{/secretToken}}
|
||||
|
||||
# {{{serverUrlHint}}}
|
||||
server_url: '{{{apmServerUrl}}}'
|
||||
|
||||
# {{{serviceEnvironmentHint}}}
|
||||
environment: 'my-environment'`;
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const railsVariables = (secretToken?: string) => ({
|
||||
apmServiceName: 'service_name',
|
||||
...(secretToken && { secretToken: 'secret_token' }),
|
||||
...(!secretToken && { apiKey: 'api_key' }),
|
||||
apmServerUrl: 'server_url',
|
||||
apmEnvironment: 'environment',
|
||||
});
|
||||
|
||||
export const railsHighlightLang = 'rb';
|
||||
|
||||
export const railsLineNumbers = () => ({
|
||||
start: 1,
|
||||
highlight: '4, 7, 10, 13',
|
||||
});
|
||||
|
||||
export const rails = `# config/elastic_apm.yml:
|
||||
|
||||
# {{serviceNameHint}} ${i18n.translate(
|
||||
'xpack.apm.onboarding.railsClient.createConfig.commands.defaultServiceName',
|
||||
{
|
||||
defaultMessage: 'Defaults to the name of your Rails app.',
|
||||
}
|
||||
)}
|
||||
service_name: 'my-service-name'
|
||||
|
||||
{{^secretToken}}
|
||||
# {{apiKeyHint}}
|
||||
api_key: '{{{apiKey}}}'
|
||||
{{/secretToken}}
|
||||
{{#secretToken}}
|
||||
# {{secretTokenHint}}
|
||||
secret_token: '{{{secretToken}}}'
|
||||
{{/secretToken}}
|
||||
|
||||
# {{{serverUrlHint}}}
|
||||
server_url: '{{{apmServerUrl}}}'
|
||||
|
||||
# {{{serviceEnvironmentHint}}}
|
||||
environment: 'my-environment'`;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const serviceNameHint = i18n.translate(
|
||||
'xpack.apm.onboarding.shared_clients.configure.commands.serviceNameHint',
|
||||
{
|
||||
defaultMessage:
|
||||
'The service name is the primary filter in the APM UI and is used to group errors and trace data together. Allowed characters are a-z, A-Z, 0-9, -, _, and space.',
|
||||
}
|
||||
);
|
||||
|
||||
export const secretTokenHint = i18n.translate(
|
||||
'xpack.apm.onboarding.shared_clients.configure.commands.secretTokenHint',
|
||||
{
|
||||
defaultMessage:
|
||||
'Use if APM Server requires a secret token. Both the agent and APM Server must be configured with the same token. This ensures that only your agents can send data to your APM server.',
|
||||
}
|
||||
);
|
||||
|
||||
export const apiKeyHint = i18n.translate(
|
||||
'xpack.apm.onboarding.shared_clients.configure.commands.apiKeyHint',
|
||||
{
|
||||
defaultMessage:
|
||||
'Use if APM Server requires an API Key. This is used to ensure that only your agents can send data to your APM server. Agents can use API keys as a replacement of secret token, APM server can have multiple API keys. When both secret token and API key are used, API key has priority and secret token is ignored.',
|
||||
}
|
||||
);
|
||||
export const serverUrlHint = i18n.translate(
|
||||
'xpack.apm.onboarding.shared_clients.configure.commands.serverUrlHint',
|
||||
{
|
||||
defaultMessage:
|
||||
'Set the custom APM Server URL (default: {defaultApmServerUrl}). The URL must be fully qualified, including protocol (http or https) and port.',
|
||||
values: { defaultApmServerUrl: 'http://localhost:8200' },
|
||||
}
|
||||
);
|
||||
|
||||
export const serviceEnvironmentHint = i18n.translate(
|
||||
'xpack.apm.onboarding.shared_clients.configure.commands.serviceEnvironmentHint',
|
||||
{
|
||||
defaultMessage: `The name of the environment this service is deployed in, e.g., "production" or "staging". Environments allow you to easily filter data on a global level in the APM UI. It's important to be consistent when naming environments across agents.`,
|
||||
}
|
||||
);
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibanaUrl } from '../../../hooks/use_kibana_url';
|
||||
|
||||
export function Footer() {
|
||||
const apmLink = useKibanaUrl('/app/apm');
|
||||
return (
|
||||
<EuiPanel paddingSize="l">
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.apm.onboarding.footer.exploreYourDataDescription"
|
||||
defaultMessage="When all steps are complete, you're ready to explore your data."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="apmTutorialFooterButton"
|
||||
fill
|
||||
href={apmLink}
|
||||
>
|
||||
{i18n.translate('xpack.apm.onboarding.footer.cta', {
|
||||
defaultMessage: 'Launch APM',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
107
x-pack/plugins/apm/public/components/app/onboarding/index.tsx
Normal file
107
x-pack/plugins/apm/public/components/app/onboarding/index.tsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { callApmApi } from '../../../services/rest/create_call_apm_api';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { ApmPluginStartDeps } from '../../../plugin';
|
||||
import { Introduction } from './introduction';
|
||||
import { InstructionsSet } from './instructions_set';
|
||||
import { serverlessInstructions } from './serverless_instructions';
|
||||
import { Footer } from './footer';
|
||||
import { PrivilegeType } from '../../../../common/privilege_type';
|
||||
import { AgentApiKey, InstructionSet } from './instruction_variants';
|
||||
|
||||
export function Onboarding() {
|
||||
const [instructions, setInstructions] = useState<InstructionSet[]>([]);
|
||||
const [agentApiKey, setAgentApiKey] = useState<AgentApiKey>({
|
||||
apiKey: null,
|
||||
error: false,
|
||||
});
|
||||
const [apiKeyLoading, setApiKeyLoading] = useState(false);
|
||||
const { services } = useKibana<ApmPluginStartDeps>();
|
||||
const { config } = useApmPluginContext();
|
||||
const { docLinks, observabilityShared } = services;
|
||||
const guideLink =
|
||||
docLinks?.links.kibana.guide ||
|
||||
'https://www.elastic.co/guide/en/kibana/current/index.html';
|
||||
|
||||
const baseUrl = docLinks?.ELASTIC_WEBSITE_URL || 'https://www.elastic.co/';
|
||||
|
||||
const createAgentKey = useCallback(async () => {
|
||||
try {
|
||||
setApiKeyLoading(true);
|
||||
const privileges: PrivilegeType[] = [PrivilegeType.EVENT];
|
||||
|
||||
const { agentKey } = await callApmApi(
|
||||
'POST /api/apm/agent_keys 2023-05-22',
|
||||
{
|
||||
signal: null,
|
||||
params: {
|
||||
body: {
|
||||
name: `onboarding-${(Math.random() + 1)
|
||||
.toString(36)
|
||||
.substring(7)}`,
|
||||
privileges,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
setAgentApiKey({
|
||||
apiKey: agentKey.api_key,
|
||||
encodedKey: agentKey.encoded,
|
||||
id: agentKey.id,
|
||||
error: false,
|
||||
});
|
||||
} catch (error) {
|
||||
setAgentApiKey({
|
||||
apiKey: null,
|
||||
error: true,
|
||||
errorMessage: error.body?.message || error.message,
|
||||
});
|
||||
} finally {
|
||||
setApiKeyLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const instructionsExists = instructions.length > 0;
|
||||
|
||||
useEffect(() => {
|
||||
// Here setInstructions will be called based on the condition for serverless, cloud or onPrem
|
||||
// right now we will only call the ServerlessInstruction directly
|
||||
setInstructions([
|
||||
serverlessInstructions(
|
||||
{
|
||||
baseUrl,
|
||||
config,
|
||||
},
|
||||
apiKeyLoading,
|
||||
agentApiKey,
|
||||
createAgentKey
|
||||
),
|
||||
]);
|
||||
}, [agentApiKey, baseUrl, config, createAgentKey, apiKeyLoading]);
|
||||
|
||||
const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate;
|
||||
return (
|
||||
<ObservabilityPageTemplate>
|
||||
<Introduction isBeta={false} guideLink={guideLink} />
|
||||
<EuiSpacer />
|
||||
{instructionsExists &&
|
||||
instructions.map((instruction) => (
|
||||
<div key={instruction.title}>
|
||||
<InstructionsSet instructions={instruction} />
|
||||
<EuiSpacer />
|
||||
</div>
|
||||
))}
|
||||
<Footer />
|
||||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
|
||||
export enum INSTRUCTION_VARIANT {
|
||||
NODE = 'node',
|
||||
DJANGO = 'django',
|
||||
FLASK = 'flask',
|
||||
RAILS = 'rails',
|
||||
RACK = 'rack',
|
||||
GO = 'go',
|
||||
JAVA = 'java',
|
||||
DOTNET = 'dotnet',
|
||||
PHP = 'php',
|
||||
OPEN_TELEMETRY = 'openTelemetry',
|
||||
}
|
||||
|
||||
export interface InstructionVariant {
|
||||
id: INSTRUCTION_VARIANT;
|
||||
instructions: EuiStepProps[];
|
||||
}
|
||||
|
||||
export interface InstructionSet {
|
||||
title: string;
|
||||
instructionVariants: InstructionVariant[];
|
||||
}
|
||||
|
||||
const DISPLAY_MAP = {
|
||||
[INSTRUCTION_VARIANT.NODE]: 'Node.js',
|
||||
[INSTRUCTION_VARIANT.DJANGO]: 'Django',
|
||||
[INSTRUCTION_VARIANT.FLASK]: 'Flask',
|
||||
[INSTRUCTION_VARIANT.RAILS]: 'Ruby on Rails',
|
||||
[INSTRUCTION_VARIANT.RACK]: 'Rack',
|
||||
[INSTRUCTION_VARIANT.GO]: 'Go',
|
||||
[INSTRUCTION_VARIANT.JAVA]: 'Java',
|
||||
[INSTRUCTION_VARIANT.DOTNET]: '.NET',
|
||||
[INSTRUCTION_VARIANT.PHP]: 'PHP',
|
||||
[INSTRUCTION_VARIANT.OPEN_TELEMETRY]: 'OpenTelemetry',
|
||||
};
|
||||
|
||||
export function getDisplayText(id: INSTRUCTION_VARIANT) {
|
||||
return id in DISPLAY_MAP ? DISPLAY_MAP[id] : id;
|
||||
}
|
||||
|
||||
export interface AgentApiKey {
|
||||
apiKey: string | null;
|
||||
id?: string;
|
||||
encodedKey?: string;
|
||||
error: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export type AgentApiDetails = AgentApiKey & {
|
||||
displayApiKeySuccessCallout: boolean;
|
||||
displayApiKeyErrorCallout: boolean;
|
||||
createAgentKey: () => void;
|
||||
createApiKeyLoading: boolean;
|
||||
};
|
||||
|
||||
export interface AgentInstructions {
|
||||
baseUrl: string;
|
||||
apmServerUrl: string;
|
||||
apiKeyDetails?: AgentApiDetails;
|
||||
secretToken?: string;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
export function ApiKeyCallout({
|
||||
isSuccess,
|
||||
isError,
|
||||
errorMessage,
|
||||
}: {
|
||||
isSuccess: boolean;
|
||||
isError: boolean;
|
||||
errorMessage?: string;
|
||||
}) {
|
||||
if (isSuccess) {
|
||||
return (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title={i18n.translate(
|
||||
'xpack.apm.onboarding.apiKey.success.calloutTitle',
|
||||
{
|
||||
defaultMessage: 'API key created',
|
||||
}
|
||||
)}
|
||||
color="success"
|
||||
iconType="check"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.apiKey.success.calloutMessage',
|
||||
{
|
||||
defaultMessage: `Remember to store this information in a safe place. It won't be displayed anymore after you continue`,
|
||||
}
|
||||
)}
|
||||
</EuiCallOut>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const regex = /missing the following requested privilege\(s\)/;
|
||||
const isInsufficientPermissionsError =
|
||||
isError && regex.test(errorMessage || '');
|
||||
|
||||
if (isInsufficientPermissionsError) {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={i18n.translate(
|
||||
'xpack.apm.onboarding.apiKey.warning.calloutTitle',
|
||||
{
|
||||
defaultMessage: 'User does not have permissions to create API Key',
|
||||
}
|
||||
)}
|
||||
color="warning"
|
||||
iconType="warning"
|
||||
>
|
||||
{i18n.translate('xpack.apm.onboarding.apiKey.warning.calloutMessage', {
|
||||
defaultMessage:
|
||||
'User is missing the following privilege - {missingPrivilege}. Please add the missing APM application privilege to the role of the authenticated user',
|
||||
values: {
|
||||
missingPrivilege: 'event:write',
|
||||
},
|
||||
})}
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.apm.onboarding.apiKey.error.calloutTitle', {
|
||||
defaultMessage: 'Failed to create API key',
|
||||
})}
|
||||
color="danger"
|
||||
iconType="error"
|
||||
>
|
||||
{i18n.translate('xpack.apm.onboarding.apiKey.error.calloutMessage', {
|
||||
defaultMessage: 'Error: {errorMessage}',
|
||||
values: {
|
||||
errorMessage,
|
||||
},
|
||||
})}
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createDjangoAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.django.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.django.install.textPre', {
|
||||
defaultMessage:
|
||||
'Install the APM agent for Python as a dependency.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
$ pip install elastic-apm
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.django.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.django.configure.textPre', {
|
||||
defaultMessage:
|
||||
'Agents are libraries that run inside of your application process. \
|
||||
APM services are created programmatically based on the `SERVICE_NAME`.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.DJANGO}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.django.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for advanced usage.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/python/current/django-support.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createDotNetAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
const codeBlock = `public class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
app.UseAllElasticApm(Configuration);
|
||||
//…rest of the method
|
||||
}
|
||||
//…rest of the class
|
||||
}`;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.dotNet.download.title', {
|
||||
defaultMessage: 'Download the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.dotNet.download.textPre', {
|
||||
defaultMessage:
|
||||
'Add the the agent package(s) from [NuGet]({allNuGetPackagesLink}) to your .NET application. There are multiple \
|
||||
NuGet packages available for different use cases. \n\nFor an ASP.NET Core application with Entity Framework \
|
||||
Core download the [Elastic.Apm.NetCoreAll]({netCoreAllApmPackageLink}) package. This package will automatically add every \
|
||||
agent component to your application. \n\n In case you would like to minimize the dependencies, you can use the \
|
||||
[Elastic.Apm.AspNetCore]({aspNetCorePackageLink}) package for just \
|
||||
ASP.NET Core monitoring or the [Elastic.Apm.EfCore]({efCorePackageLink}) package for just Entity Framework Core monitoring. \n\n In \
|
||||
case you only want to use the public Agent API for manual instrumentation use the [Elastic.Apm]({elasticApmPackageLink}) package.',
|
||||
values: {
|
||||
allNuGetPackagesLink:
|
||||
'https://www.nuget.org/packages?q=Elastic.apm',
|
||||
netCoreAllApmPackageLink:
|
||||
'https://www.nuget.org/packages/Elastic.Apm.NetCoreAll',
|
||||
aspNetCorePackageLink:
|
||||
'https://www.nuget.org/packages/Elastic.Apm.AspNetCore',
|
||||
efCorePackageLink:
|
||||
'https://www.nuget.org/packages/Elastic.Apm.EntityFrameworkCore',
|
||||
elasticApmPackageLink:
|
||||
'https://www.nuget.org/packages/Elastic.Apm',
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.onboarding.dotNet.configureApplication.title',
|
||||
{
|
||||
defaultMessage: 'Add the agent to the application',
|
||||
}
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.dotNet.configureApplication.textPre',
|
||||
{
|
||||
defaultMessage:
|
||||
'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `UseAllElasticApm` \
|
||||
method in the `Configure` method within the `Startup.cs` file.',
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
{codeBlock}
|
||||
</EuiCodeBlock>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.dotNet.configureApplication.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'Passing an `IConfiguration` instance is optional and by doing so, the agent will read config settings through this \
|
||||
`IConfiguration` instance (e.g. from the `appsettings.json` file).',
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.onboarding.dotNet.configureAgent.title',
|
||||
{
|
||||
defaultMessage: 'Sample appsettings.json file:',
|
||||
}
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.DOTNET}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.dotNet.configureAgent.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'In case you don’t pass an `IConfiguration` instance to the agent (e.g. in case of non ASP.NET Core applications) \
|
||||
you can also configure the agent through environment variables. \n \
|
||||
See [the documentation]({documentationLink}) for advanced usage, including the [Profiler Auto instrumentation]({profilerLink}) quick start.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/dotnet/current/configuration.html`,
|
||||
profilerLink: `${baseUrl}guide/en/apm/agent/dotnet/current/setup-auto-instrumentation.html#setup-auto-instrumentation`,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createFlaskAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.flask.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.flask.install.textPre', {
|
||||
defaultMessage:
|
||||
'Install the APM agent for Python as a dependency.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
$ pip install elastic-apm[flask]
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.flask.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.flask.configure.textPre', {
|
||||
defaultMessage:
|
||||
'Agents are libraries that run inside of your application process. \
|
||||
APM services are created programmatically based on the `SERVICE_NAME`.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.FLASK}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.flask.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for advanced usage.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/python/current/flask-support.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createGoAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
const codeBlock = `\
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.elastic.co/apm/module/apmhttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
...
|
||||
http.ListenAndServe(":8080", apmhttp.Wrap(mux))
|
||||
}
|
||||
`;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.go.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.go.install.textPre', {
|
||||
defaultMessage: 'Install the APM agent packages for Go.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
go get go.elastic.co/apm
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.go.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.go.configure.textPre', {
|
||||
defaultMessage:
|
||||
'Agents are libraries that run inside of your application process. \
|
||||
APM services are created programmatically based on the executable \
|
||||
file name, or the `ELASTIC_APM_SERVICE_NAME` environment variable.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.GO}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.go.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for advanced configuration.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/go/current/configuration.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.go.goClient.title', {
|
||||
defaultMessage: 'Instrument your application',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.go.instrument.textPre', {
|
||||
defaultMessage:
|
||||
'Instrument your Go application by using one of the provided instrumentation modules or \
|
||||
by using the tracer API directly.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="go" isCopyable={true}>
|
||||
{codeBlock}
|
||||
</EuiCodeBlock>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.go.instrument.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for a detailed \
|
||||
guide to instrumenting Go source code.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/go/current/instrumenting-source.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { createJavaAgentInstructions } from './java_agent';
|
||||
export { createNodeAgentInstructions } from './node_agent';
|
||||
export { createDjangoAgentInstructions } from './django_agent';
|
||||
export { createFlaskAgentInstructions } from './flask_agent';
|
||||
export { createRailsAgentInstructions } from './rails_agent';
|
||||
export { createRackAgentInstructions } from './rack_agent';
|
||||
export { createGoAgentInstructions } from './go_agent';
|
||||
export { createDotNetAgentInstructions } from './dotnet_agent';
|
||||
export { createPhpAgentInstructions } from './php_agent';
|
||||
export { createOpenTelemetryAgentInstructions } from './otel_agent';
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createJavaAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.java.download.title', {
|
||||
defaultMessage: 'Download the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.java.download.textPre', {
|
||||
defaultMessage:
|
||||
'Download the agent jar from [Maven Central]({mavenCentralLink}). \
|
||||
Do **not** add the agent as a dependency to your application.',
|
||||
values: {
|
||||
mavenCentralLink:
|
||||
'https://oss.sonatype.org/service/local/artifact/maven/redirect?r=releases&g=co.elastic.apm&a=elastic-apm-agent&v=LATEST',
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.onboarding.java.startApplication.title',
|
||||
{
|
||||
defaultMessage: 'Start your application with the javaagent flag',
|
||||
}
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.java.startApplication.textPre',
|
||||
{
|
||||
defaultMessage:
|
||||
'Add the `-javaagent` flag and configure the agent with system properties.\n\n \
|
||||
* Set the required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)\n \
|
||||
* Set the custom APM Server URL (default: {customApmServerUrl})\n \
|
||||
* Set the APM Server secret token\n \
|
||||
* Set the service environment\n \
|
||||
* Set the base package of your application',
|
||||
values: { customApmServerUrl: 'http://localhost:8200' },
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.JAVA}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.java.startApplication.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for configuration options and advanced \
|
||||
usage.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/java/current/index.html`,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
|
||||
export const createNodeAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.node.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.node.install.textPre', {
|
||||
defaultMessage:
|
||||
'Install the APM agent for Node.js as a dependency to your application.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
npm install elastic-apm-node --save
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.node.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.node.configure.textPre', {
|
||||
defaultMessage:
|
||||
'Agents are libraries that run inside of your application process. \
|
||||
APM services are created programmatically based on the `serviceName`. \
|
||||
This agent supports a variety of frameworks but can also be used with your custom stack.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.NODE}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.node.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See [the documentation]({documentationLink}) for advanced usage, including how to use with \
|
||||
[Babel/ES Modules]({babelEsModulesLink}).',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/nodejs/current/index.html`,
|
||||
babelEsModulesLink: `${baseUrl}guide/en/apm/agent/nodejs/current/advanced-setup.html#es-modules`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiBasicTableColumn,
|
||||
EuiButton,
|
||||
EuiLink,
|
||||
EuiMarkdownFormat,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { ValuesType } from 'utility-types';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { AgentApiDetails, AgentInstructions } from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createOpenTelemetryAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.otel.download.title', {
|
||||
defaultMessage: 'Download the OpenTelemetry APM Agent or SDK',
|
||||
}),
|
||||
children: (
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.otel.download.textPre', {
|
||||
defaultMessage:
|
||||
'See the [OpenTelemetry Instrumentation guides]({openTelemetryInstrumentationLink}) to download the OpenTelemetry Agent or SDK for your language.',
|
||||
values: {
|
||||
openTelemetryInstrumentationLink:
|
||||
'https://opentelemetry.io/docs/instrumentation',
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.otel.configureAgent.title', {
|
||||
defaultMessage: 'Configure OpenTelemetry in your application',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.otel.configureAgent.textPre',
|
||||
{
|
||||
defaultMessage:
|
||||
'Specify the following OpenTelemetry settings as part of the startup of your application. Note that OpenTelemetry SDKs require some bootstrap code in addition to these configuration settings. For more details, see the [Elastic OpenTelemetry documentation]({openTelemetryDocumentationLink}) and the [OpenTelemetry community instrumentation guides]({openTelemetryInstrumentationLink}).',
|
||||
values: {
|
||||
openTelemetryDocumentationLink: `${baseUrl}guide/en/apm/guide/current/open-telemetry.html`,
|
||||
openTelemetryInstrumentationLink:
|
||||
'https://opentelemetry.io/docs/instrumentation',
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<OpenTelemetryInstructions
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKeyDetails={apiKeyDetails}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.otel.configureAgent.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/guide/current/open-telemetry.html`,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
function ConfigurationValueColumn({
|
||||
setting,
|
||||
value,
|
||||
createApiKey,
|
||||
createApiKeyLoading,
|
||||
apiKey,
|
||||
}: {
|
||||
setting: string;
|
||||
value: string;
|
||||
createApiKey?: () => void;
|
||||
createApiKeyLoading?: boolean;
|
||||
apiKey?: string | null;
|
||||
}) {
|
||||
const shouldRenderCreateApiKeyButton =
|
||||
setting === 'OTEL_EXPORTER_OTLP_HEADERS' && apiKey === null;
|
||||
|
||||
if (shouldRenderCreateApiKeyButton) {
|
||||
return (
|
||||
<EuiButton
|
||||
data-test-subj="createApiKeyAndId"
|
||||
fill
|
||||
onClick={createApiKey}
|
||||
isLoading={createApiKeyLoading}
|
||||
>
|
||||
{i18n.translate('xpack.apm.onboarding.apiKey.create', {
|
||||
defaultMessage: 'Create API Key',
|
||||
})}
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiText size="s" color="accent">
|
||||
{value}
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
||||
export function OpenTelemetryInstructions({
|
||||
apmServerUrl,
|
||||
secretToken,
|
||||
apiKeyDetails,
|
||||
}: {
|
||||
apmServerUrl: string;
|
||||
secretToken?: string;
|
||||
apiKeyDetails?: AgentApiDetails;
|
||||
}) {
|
||||
let authHeaderValue;
|
||||
|
||||
if (secretToken) {
|
||||
authHeaderValue = `Authorization=Bearer ${secretToken}`;
|
||||
} else {
|
||||
authHeaderValue = `Authorization=ApiKey ${apiKeyDetails?.encodedKey}`;
|
||||
}
|
||||
const items = [
|
||||
{
|
||||
setting: 'OTEL_EXPORTER_OTLP_ENDPOINT',
|
||||
value: apmServerUrl ? apmServerUrl : '<apm-server-url>',
|
||||
},
|
||||
{
|
||||
setting: 'OTEL_EXPORTER_OTLP_HEADERS',
|
||||
value: authHeaderValue,
|
||||
apiKey: apiKeyDetails?.apiKey,
|
||||
},
|
||||
{
|
||||
setting: 'OTEL_METRICS_EXPORTER',
|
||||
value: 'otlp',
|
||||
notes: 'Enable metrics when supported by your OpenTelemetry client.',
|
||||
},
|
||||
{
|
||||
setting: 'OTEL_LOGS_EXPORTER',
|
||||
value: 'otlp',
|
||||
notes: 'Enable logs when supported by your OpenTelemetry client',
|
||||
},
|
||||
{
|
||||
setting: 'OTEL_RESOURCE_ATTRIBUTES',
|
||||
value:
|
||||
'service.name=<app-name>,service.version=<app-version>,deployment.environment=production',
|
||||
},
|
||||
];
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<ValuesType<typeof items>>> = [
|
||||
{
|
||||
field: 'setting',
|
||||
name: i18n.translate(
|
||||
'xpack.apm.onboarding.config_otel.column.configSettings',
|
||||
{
|
||||
defaultMessage: 'Configuration setting (1)',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
name: i18n.translate(
|
||||
'xpack.apm.onboarding.config_otel.column.configValue',
|
||||
{
|
||||
defaultMessage: 'Configuration value',
|
||||
}
|
||||
),
|
||||
render: (_, { value, setting, apiKey }) => (
|
||||
<ConfigurationValueColumn
|
||||
setting={setting}
|
||||
value={value}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
name: i18n.translate('xpack.apm.onboarding.config_otel.column.notes', {
|
||||
defaultMessage: 'Notes',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiBasicTable
|
||||
items={items}
|
||||
columns={columns}
|
||||
data-test-subj="otel-instructions-table"
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="xs" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.apm.onboarding.config_otel.description1"
|
||||
defaultMessage="(1) OpenTelemetry agents and SDKs must support the {otelExporterOtlpEndpoint}, {otelExporterOtlpHeaders}, and {otelResourceAttributes} variables; some unstable components may not yet comply with this requirement."
|
||||
values={{
|
||||
otelExporterOtlpEndpoint: (
|
||||
<EuiLink
|
||||
data-test-subj="apmOpenTelemetryInstructionsOtelExporterOtlpEndpointLink"
|
||||
target="_blank"
|
||||
href="https://github.com/open-telemetry/opentelemetry-specification/blob/v1.10.0/specification/protocol/exporter.md"
|
||||
>
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
</EuiLink>
|
||||
),
|
||||
otelExporterOtlpHeaders: (
|
||||
<EuiLink
|
||||
data-test-subj="apmOpenTelemetryInstructionsOtelExporterOtlpHeadersLink"
|
||||
target="_blank"
|
||||
href="https://github.com/open-telemetry/opentelemetry-specification/blob/v1.10.0/specification/protocol/exporter.md"
|
||||
>
|
||||
OTEL_EXPORTER_OTLP_HEADERS
|
||||
</EuiLink>
|
||||
),
|
||||
otelResourceAttributes: (
|
||||
<EuiLink
|
||||
data-test-subj="apmOpenTelemetryInstructionsOtelResourceAttributesLink"
|
||||
target="_blank"
|
||||
href="https://github.com/open-telemetry/opentelemetry-specification/blob/v1.10.0/specification/resource/sdk.md"
|
||||
>
|
||||
OTEL_RESOURCE_ATTRIBUTES
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer size="xs" />
|
||||
<FormattedMessage
|
||||
id="xpack.apm.onboarding.config_otel.description2"
|
||||
defaultMessage="The 'OTEL_METRICS_EXPORTER` and 'OTEL_LOGS_EXPORTER' environment variables may not be supported by some SDKs."
|
||||
/>
|
||||
<EuiSpacer size="xs" />
|
||||
<FormattedMessage
|
||||
id="xpack.apm.onboarding.config_otel.description3"
|
||||
defaultMessage="The exhaustive list of environment variables, command line parameters, and configuration code snippets (according to the OpenTelemetry specification) is available in the {otelInstrumentationGuide}. Some unstable OpenTelemetry clients may not support all features and may require alternate configuration mechanisms."
|
||||
values={{
|
||||
otelInstrumentationGuide: (
|
||||
<EuiLink
|
||||
data-test-subj="apmOpenTelemetryInstructionsOpenTelemetryInstrumentationGuideLink"
|
||||
target="_blank"
|
||||
href="https://opentelemetry.io/docs/instrumentation"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.config_otel.instrumentationGuide',
|
||||
{
|
||||
defaultMessage: 'OpenTelemetry Instrumentation guide',
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createPhpAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.php.download.title', {
|
||||
defaultMessage: 'Download the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.php.download.textPre', {
|
||||
defaultMessage:
|
||||
'Download the package corresponding to your platform from [GitHub releases]({githubReleasesLink}).',
|
||||
values: {
|
||||
githubReleasesLink:
|
||||
'https://github.com/elastic/apm-agent-php/releases',
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.php.installPackage.title', {
|
||||
defaultMessage: 'Install the downloaded package',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.php.installPackage.textPre', {
|
||||
defaultMessage: 'For example on Alpine Linux using APK package:',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
apk add --allow-untrusted <package-file>.apk
|
||||
</EuiCodeBlock>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.php.installPackage.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for installation commands on other supported platforms and advanced installation.',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/php/current/setup.html`,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.php.configureAgent.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.php.Configure the agent.textPre',
|
||||
{
|
||||
defaultMessage:
|
||||
'APM is automatically started when your app boots. Configure the agent either via `php.ini` file:',
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.PHP}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate(
|
||||
'xpack.apm.onboarding.php.configureAgent.textPost',
|
||||
{
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/php/current/configuration.html`,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createRackAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
const codeBlock = `# config.ru
|
||||
require 'sinatra/base'
|
||||
|
||||
class MySinatraApp < Sinatra::Base
|
||||
use ElasticAPM::Middleware
|
||||
|
||||
# ...
|
||||
end
|
||||
|
||||
ElasticAPM.start(
|
||||
app: MySinatraApp, # ${i18n.translate(
|
||||
'xpack.apm.onboarding.rack.configure.commands.requiredComment',
|
||||
{
|
||||
defaultMessage: 'required',
|
||||
}
|
||||
)}
|
||||
config_file: '' # ${i18n.translate(
|
||||
'xpack.apm.onboarding.rack.configure.commands.optionalComment',
|
||||
{
|
||||
defaultMessage: 'optional, defaults to config/elastic_apm.yml',
|
||||
}
|
||||
)}
|
||||
)
|
||||
|
||||
run MySinatraApp
|
||||
|
||||
at_exit { ElasticAPM.stop }`;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.rack.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rack.install.textPre', {
|
||||
defaultMessage: 'Add the agent to your Gemfile.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
gem 'elastic-apm'
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.rack.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rack.configure.textPre', {
|
||||
defaultMessage:
|
||||
'For Rack or a compatible framework (e.g. Sinatra), include the middleware in your app and start the agent.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
{codeBlock}
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.rack.createConfig.title', {
|
||||
defaultMessage: 'Create config file',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rack.createConfig.textPre', {
|
||||
defaultMessage: 'Create a config file {configFile}:',
|
||||
values: { configFile: '`config/elastic_apm.yml`' },
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.RACK}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rack.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/ruby/current/index.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCodeBlock, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiStepProps } from '@elastic/eui/src/components/steps/step';
|
||||
import React from 'react';
|
||||
import { AgentConfigInstructions } from '../agent_config_instructions';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
} from '../instruction_variants';
|
||||
import { ApiKeyCallout } from './api_key_callout';
|
||||
|
||||
export const createRailsAgentInstructions = (
|
||||
commonOptions: AgentInstructions
|
||||
): EuiStepProps[] => {
|
||||
const { baseUrl, apmServerUrl, apiKeyDetails } = commonOptions;
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.rails.install.title', {
|
||||
defaultMessage: 'Install the APM agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rails.install.textPre', {
|
||||
defaultMessage: 'Add the agent to your Gemfile.',
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock language="bash" isCopyable={true}>
|
||||
gem 'elastic-apm'
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.apm.onboarding.rails.configure.title', {
|
||||
defaultMessage: 'Configure the agent',
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rails.configure.textPre', {
|
||||
defaultMessage:
|
||||
'APM is automatically started when your app boots. Configure the agent, by creating the config file {configFile}',
|
||||
values: { configFile: '`config/elastic_apm.yml`' },
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
<EuiSpacer />
|
||||
|
||||
{(apiKeyDetails?.displayApiKeySuccessCallout ||
|
||||
apiKeyDetails?.displayApiKeyErrorCallout) && (
|
||||
<>
|
||||
<ApiKeyCallout
|
||||
isError={apiKeyDetails?.displayApiKeyErrorCallout}
|
||||
isSuccess={apiKeyDetails?.displayApiKeySuccessCallout}
|
||||
errorMessage={apiKeyDetails?.errorMessage}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<AgentConfigInstructions
|
||||
variantId={INSTRUCTION_VARIANT.RAILS}
|
||||
apmServerUrl={apmServerUrl}
|
||||
apiKey={apiKeyDetails?.apiKey}
|
||||
createApiKey={apiKeyDetails?.createAgentKey}
|
||||
createApiKeyLoading={apiKeyDetails?.createApiKeyLoading}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiMarkdownFormat>
|
||||
{i18n.translate('xpack.apm.onboarding.rails.configure.textPost', {
|
||||
defaultMessage:
|
||||
'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
|
||||
values: {
|
||||
documentationLink: `${baseUrl}guide/en/apm/agent/ruby/current/index.html`,
|
||||
},
|
||||
})}
|
||||
</EuiMarkdownFormat>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiSplitPanel,
|
||||
EuiTabs,
|
||||
EuiTab,
|
||||
EuiTitle,
|
||||
EuiSteps,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
getDisplayText,
|
||||
InstructionVariant,
|
||||
InstructionSet,
|
||||
} from './instruction_variants';
|
||||
|
||||
interface AgentTab {
|
||||
id: INSTRUCTION_VARIANT;
|
||||
text: string;
|
||||
}
|
||||
|
||||
function getTabs(variants: InstructionVariant[]): AgentTab[] {
|
||||
return variants.map((variant) => ({
|
||||
id: variant.id,
|
||||
text: getDisplayText(variant.id),
|
||||
}));
|
||||
}
|
||||
|
||||
export function InstructionsSet({
|
||||
instructions,
|
||||
}: {
|
||||
instructions: InstructionSet;
|
||||
}) {
|
||||
const tabs = getTabs(instructions.instructionVariants);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<string>(tabs[0].id);
|
||||
const onSelectedTabChange = (tab: string) => {
|
||||
setSelectedTab(tab);
|
||||
};
|
||||
|
||||
function InstructionTabs({ agentTabs }: { agentTabs: AgentTab[] }) {
|
||||
return (
|
||||
<EuiTabs>
|
||||
{agentTabs.map((tab) => (
|
||||
<EuiTab
|
||||
key={tab.id}
|
||||
isSelected={tab.id === selectedTab}
|
||||
onClick={() => onSelectedTabChange(tab.id)}
|
||||
>
|
||||
{tab.text}
|
||||
</EuiTab>
|
||||
))}
|
||||
</EuiTabs>
|
||||
);
|
||||
}
|
||||
|
||||
function InstructionSteps({
|
||||
instructionVariants,
|
||||
tab,
|
||||
}: {
|
||||
instructionVariants: InstructionVariant[];
|
||||
tab: string;
|
||||
}) {
|
||||
const selectInstructionSteps = instructionVariants.find((variant) => {
|
||||
return variant.id === tab;
|
||||
});
|
||||
|
||||
if (!selectInstructionSteps) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiSteps titleSize="xs" steps={selectInstructionSteps.instructions} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiSplitPanel.Outer>
|
||||
<EuiSplitPanel.Inner color="subdued" paddingSize="none">
|
||||
<InstructionTabs agentTabs={tabs} />
|
||||
</EuiSplitPanel.Inner>
|
||||
<EuiSplitPanel.Inner paddingSize="l">
|
||||
<EuiTitle size="m">
|
||||
<h2>{instructions.title}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<InstructionSteps
|
||||
instructionVariants={instructions.instructionVariants}
|
||||
tab={selectedTab}
|
||||
/>
|
||||
</EuiSplitPanel.Inner>
|
||||
</EuiSplitPanel.Outer>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiBetaBadge,
|
||||
EuiImage,
|
||||
EuiMarkdownFormat,
|
||||
EuiPageHeader,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibanaUrl } from '../../../hooks/use_kibana_url';
|
||||
|
||||
interface IntroductionProps {
|
||||
isBeta?: boolean;
|
||||
guideLink: string;
|
||||
}
|
||||
|
||||
export function Introduction({ isBeta, guideLink }: IntroductionProps) {
|
||||
const betaBadge = (
|
||||
<EuiBetaBadge
|
||||
label={i18n.translate('xpack.apm.onboarding.betaLabel', {
|
||||
defaultMessage: 'Beta',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
const previewImage = useKibanaUrl('/plugins/apm/assets/apm.png');
|
||||
|
||||
const rightSideItems = [
|
||||
<EuiImage
|
||||
size="l"
|
||||
allowFullScreen
|
||||
fullScreenIconColor="dark"
|
||||
alt={i18n.translate(
|
||||
'xpack.apm.onboarding.introduction.imageAltDescription',
|
||||
{
|
||||
defaultMessage: 'screenshot of primary dashboard.',
|
||||
}
|
||||
)}
|
||||
url={previewImage}
|
||||
/>,
|
||||
];
|
||||
|
||||
const description = i18n.translate(
|
||||
'xpack.apm.onboarding.specProvider.longDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Application Performance Monitoring (APM) collects in-depth \
|
||||
performance metrics and errors from inside your application. \
|
||||
It allows you to monitor the performance of thousands of applications in real time. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: guideLink,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPageHeader
|
||||
iconType="apmApp"
|
||||
pageTitle={
|
||||
<>
|
||||
{i18n.translate('xpack.apm.onboarding.appName', {
|
||||
defaultMessage: 'APM',
|
||||
})}
|
||||
{isBeta && (
|
||||
<>
|
||||
|
||||
{betaBadge}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
description={<EuiMarkdownFormat>{description}</EuiMarkdownFormat>}
|
||||
rightSideItems={rightSideItems}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ConfigSchema } from '../../..';
|
||||
import {
|
||||
INSTRUCTION_VARIANT,
|
||||
AgentInstructions,
|
||||
AgentApiKey,
|
||||
} from './instruction_variants';
|
||||
import {
|
||||
createJavaAgentInstructions,
|
||||
createNodeAgentInstructions,
|
||||
createDjangoAgentInstructions,
|
||||
createFlaskAgentInstructions,
|
||||
createRailsAgentInstructions,
|
||||
createRackAgentInstructions,
|
||||
createGoAgentInstructions,
|
||||
createDotNetAgentInstructions,
|
||||
createPhpAgentInstructions,
|
||||
createOpenTelemetryAgentInstructions,
|
||||
} from './instructions';
|
||||
|
||||
export function serverlessInstructions(
|
||||
{
|
||||
baseUrl,
|
||||
config,
|
||||
}: {
|
||||
baseUrl: string;
|
||||
config: ConfigSchema;
|
||||
},
|
||||
apiKeyLoading: boolean,
|
||||
apiKeyDetails: AgentApiKey,
|
||||
createAgentKey: () => void
|
||||
) {
|
||||
const { apiKey, error, errorMessage } = apiKeyDetails;
|
||||
const displayApiKeySuccessCallout = Boolean(apiKey) && !error;
|
||||
const displayApiKeyErrorCallout = error && Boolean(errorMessage);
|
||||
const commonOptions: AgentInstructions = {
|
||||
baseUrl,
|
||||
apmServerUrl: config.managedServiceUrl,
|
||||
apiKeyDetails: {
|
||||
...apiKeyDetails,
|
||||
displayApiKeySuccessCallout,
|
||||
displayApiKeyErrorCallout,
|
||||
createAgentKey,
|
||||
createApiKeyLoading: apiKeyLoading,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
title: i18n.translate('xpack.apm.tutorial.apmAgents.title', {
|
||||
defaultMessage: 'APM Agents',
|
||||
}),
|
||||
instructionVariants: [
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.NODE,
|
||||
instructions: createNodeAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.DJANGO,
|
||||
instructions: createDjangoAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.FLASK,
|
||||
instructions: createFlaskAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.RAILS,
|
||||
instructions: createRailsAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.RACK,
|
||||
instructions: createRackAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.GO,
|
||||
instructions: createGoAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.JAVA,
|
||||
instructions: createJavaAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.DOTNET,
|
||||
instructions: createDotNetAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.PHP,
|
||||
instructions: createPhpAgentInstructions(commonOptions),
|
||||
},
|
||||
{
|
||||
id: INSTRUCTION_VARIANT.OPEN_TELEMETRY,
|
||||
instructions: createOpenTelemetryAgentInstructions(commonOptions),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
|
@ -17,6 +17,7 @@ import { homeRoute } from './home';
|
|||
import { serviceDetailRoute } from './service_detail';
|
||||
import { mobileServiceDetailRoute } from './mobile_service_detail';
|
||||
import { settingsRoute } from './settings';
|
||||
import { onboarding } from './onboarding';
|
||||
import { ApmMainTemplate } from './templates/apm_main_template';
|
||||
import { ServiceGroupsList } from '../app/service_groups';
|
||||
import { offsetRt } from '../../../common/comparison_rt';
|
||||
|
@ -105,6 +106,7 @@ const apmRoutes = {
|
|||
]),
|
||||
}),
|
||||
},
|
||||
...onboarding,
|
||||
...diagnosticsRoute,
|
||||
...settingsRoute,
|
||||
...serviceDetailRoute,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Onboarding } from '../../app/onboarding';
|
||||
|
||||
export const onboarding = {
|
||||
'/onboarding': {
|
||||
element: <Onboarding />,
|
||||
},
|
||||
};
|
|
@ -18,6 +18,7 @@ import { ServiceGroupSaveButton } from '../../app/service_groups';
|
|||
import { ServiceGroupsButtonGroup } from '../../app/service_groups/service_groups_button_group';
|
||||
import { ApmEnvironmentFilter } from '../../shared/environment_filter';
|
||||
import { getNoDataConfig } from './no_data_config';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
|
||||
// Paths that must skip the no data screen
|
||||
const bypassNoDataScreenPaths = ['/settings', '/diagnostics'];
|
||||
|
@ -56,6 +57,7 @@ export function ApmMainTemplate({
|
|||
const { services } = useKibana<ApmPluginStartDeps>();
|
||||
const { http, docLinks, observabilityShared, application } = services;
|
||||
const basePath = http?.basePath.get();
|
||||
const { config } = useApmPluginContext();
|
||||
|
||||
const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate;
|
||||
|
||||
|
@ -101,6 +103,7 @@ export function ApmMainTemplate({
|
|||
hasApmIntegrations: fleetApmPoliciesData?.hasApmPolicies,
|
||||
shouldBypassNoDataScreen,
|
||||
loading: isLoading,
|
||||
isServerless: config?.serverlessOnboarding,
|
||||
});
|
||||
|
||||
const rightSideItems = [
|
||||
|
|
|
@ -8,6 +8,56 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { NoDataConfig } from '@kbn/shared-ux-page-kibana-template';
|
||||
|
||||
function getNoDataConfigDetails({
|
||||
basePath,
|
||||
isServerless,
|
||||
hasApmIntegrations,
|
||||
}: {
|
||||
basePath?: string;
|
||||
isServerless?: boolean;
|
||||
hasApmIntegrations?: boolean;
|
||||
}) {
|
||||
const description = i18n.translate(
|
||||
'xpack.apm.ux.overview.agent.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Use APM agents to collect APM data. We make it easy with agents for many popular languages.',
|
||||
}
|
||||
);
|
||||
|
||||
const addDataTitle = i18n.translate(
|
||||
'xpack.apm.noDataConfig.addDataButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Add data',
|
||||
}
|
||||
);
|
||||
|
||||
if (isServerless) {
|
||||
return {
|
||||
title: addDataTitle,
|
||||
href: `${basePath}/app/apm/onboarding`,
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
if (hasApmIntegrations) {
|
||||
return {
|
||||
title: addDataTitle,
|
||||
href: `${basePath}/app/home#/tutorial/apm`,
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: i18n.translate(
|
||||
'xpack.apm.noDataConfig.addApmIntegrationButtonLabel',
|
||||
{ defaultMessage: 'Add the APM integration' }
|
||||
),
|
||||
href: `${basePath}/app/integrations/detail/apm/overview`,
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
export function getNoDataConfig({
|
||||
docsLink,
|
||||
shouldBypassNoDataScreen,
|
||||
|
@ -15,6 +65,7 @@ export function getNoDataConfig({
|
|||
basePath,
|
||||
hasApmData,
|
||||
hasApmIntegrations,
|
||||
isServerless,
|
||||
}: {
|
||||
docsLink: string;
|
||||
shouldBypassNoDataScreen: boolean;
|
||||
|
@ -22,27 +73,17 @@ export function getNoDataConfig({
|
|||
basePath?: string;
|
||||
hasApmData?: boolean;
|
||||
hasApmIntegrations?: boolean;
|
||||
isServerless?: boolean;
|
||||
}): NoDataConfig | undefined {
|
||||
// don't show "no data screen" when there is APM data or it should be bypassed
|
||||
if (hasApmData || shouldBypassNoDataScreen || loading) {
|
||||
return;
|
||||
}
|
||||
const noDataConfigDetails = hasApmIntegrations
|
||||
? {
|
||||
title: i18n.translate('xpack.apm.noDataConfig.addDataButtonLabel', {
|
||||
defaultMessage: 'Add data',
|
||||
}),
|
||||
href: `${basePath}/app/home#/tutorial/apm`,
|
||||
}
|
||||
: {
|
||||
title: i18n.translate(
|
||||
'xpack.apm.noDataConfig.addApmIntegrationButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Add the APM integration',
|
||||
}
|
||||
),
|
||||
href: `${basePath}/app/integrations/detail/apm/overview`,
|
||||
};
|
||||
const noDataConfigDetails = getNoDataConfigDetails({
|
||||
basePath,
|
||||
isServerless,
|
||||
hasApmIntegrations,
|
||||
});
|
||||
|
||||
return {
|
||||
solution: i18n.translate('xpack.apm.noDataConfig.solutionName', {
|
||||
|
@ -51,10 +92,7 @@ export function getNoDataConfig({
|
|||
action: {
|
||||
elasticAgent: {
|
||||
title: noDataConfigDetails.title,
|
||||
description: i18n.translate('xpack.apm.ux.overview.agent.description', {
|
||||
defaultMessage:
|
||||
'Use APM agents to collect APM data. We make it easy with agents for many popular languages.',
|
||||
}),
|
||||
description: noDataConfigDetails.description,
|
||||
href: noDataConfigDetails.href,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -69,6 +69,8 @@ const mockConfig: ConfigSchema = {
|
|||
enabled: false,
|
||||
},
|
||||
latestAgentVersionsUrl: '',
|
||||
serverlessOnboarding: false,
|
||||
managedServiceUrl: '',
|
||||
};
|
||||
|
||||
const urlService = new UrlService({
|
||||
|
|
|
@ -14,6 +14,8 @@ export interface ConfigSchema {
|
|||
enabled: boolean;
|
||||
};
|
||||
latestAgentVersionsUrl: string;
|
||||
serverlessOnboarding: boolean;
|
||||
managedServiceUrl: string;
|
||||
}
|
||||
|
||||
export const plugin: PluginInitializer<ApmPluginSetup, ApmPluginStart> = (
|
||||
|
|
|
@ -57,6 +57,18 @@ const configSchema = schema.object({
|
|||
defaultValue: 'https://apm-agent-versions.elastic.co/versions.json',
|
||||
}),
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
serverlessOnboarding: schema.conditional(
|
||||
schema.contextRef('serverless'),
|
||||
true,
|
||||
schema.boolean({ defaultValue: false }),
|
||||
schema.never()
|
||||
),
|
||||
managedServiceUrl: schema.conditional(
|
||||
schema.contextRef('serverless'),
|
||||
true,
|
||||
schema.string({ defaultValue: '' }),
|
||||
schema.never()
|
||||
),
|
||||
});
|
||||
|
||||
// plugin config
|
||||
|
@ -115,6 +127,8 @@ export const config: PluginConfigDescriptor<APMConfig> = {
|
|||
serviceMapEnabled: true,
|
||||
ui: true,
|
||||
latestAgentVersionsUrl: true,
|
||||
managedServiceUrl: true,
|
||||
serverlessOnboarding: true,
|
||||
},
|
||||
schema: configSchema,
|
||||
};
|
||||
|
|
|
@ -56,6 +56,7 @@ import { scheduleSourceMapMigration } from './routes/source_maps/schedule_source
|
|||
import { createApmSourceMapIndexTemplate } from './routes/source_maps/create_apm_source_map_index_template';
|
||||
import { addApiKeysToEveryPackagePolicyIfMissing } from './routes/fleet/api_keys/add_api_keys_to_policies_if_missing';
|
||||
import { getApmFeatureFlags } from '../common/apm_feature_flags';
|
||||
import { apmTutorialCustomIntegration } from '../common/tutorial/tutorials';
|
||||
|
||||
export class APMPlugin
|
||||
implements
|
||||
|
@ -68,6 +69,7 @@ export class APMPlugin
|
|||
{
|
||||
private currentConfig?: APMConfig;
|
||||
private logger?: Logger;
|
||||
|
||||
constructor(private readonly initContext: PluginInitializerContext) {
|
||||
this.initContext = initContext;
|
||||
}
|
||||
|
@ -148,16 +150,26 @@ export class APMPlugin
|
|||
});
|
||||
};
|
||||
|
||||
boundGetApmIndices().then((indices) => {
|
||||
plugins.home?.tutorials.registerTutorial(
|
||||
tutorialProvider({
|
||||
apmConfig: currentConfig,
|
||||
apmIndices: indices,
|
||||
cloud: plugins.cloud,
|
||||
isFleetPluginEnabled: !isEmpty(resourcePlugins.fleet),
|
||||
})
|
||||
// This if else block will go away in favour of removing Home Tutorial Integration
|
||||
// Ideally we will directly register a custom integration and pass the configs
|
||||
// for cloud, onPrem and Serverless so that the actual component can take
|
||||
// care of rendering
|
||||
if (currentConfig.serverlessOnboarding && plugins.customIntegrations) {
|
||||
plugins.customIntegrations?.registerCustomIntegration(
|
||||
apmTutorialCustomIntegration
|
||||
);
|
||||
});
|
||||
} else {
|
||||
boundGetApmIndices().then((indices) => {
|
||||
plugins.home?.tutorials.registerTutorial(
|
||||
tutorialProvider({
|
||||
apmConfig: currentConfig,
|
||||
apmIndices: indices,
|
||||
cloud: plugins.cloud,
|
||||
isFleetPluginEnabled: !isEmpty(resourcePlugins.fleet),
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const telemetryUsageCounter =
|
||||
resourcePlugins.usageCollection?.setup.createUsageCounter(
|
||||
|
|
|
@ -68,7 +68,7 @@ export async function createAgentKey({
|
|||
}],
|
||||
...
|
||||
}`;
|
||||
throw Boom.internal(error);
|
||||
throw Boom.internal(error, { missingPrivileges }, 403);
|
||||
}
|
||||
|
||||
const body = {
|
||||
|
|
|
@ -257,7 +257,7 @@ describe('createApi', () => {
|
|||
expect(response.ok).not.toHaveBeenCalled();
|
||||
expect(response.custom).toHaveBeenCalledWith({
|
||||
body: {
|
||||
attributes: { _inspect: [] },
|
||||
attributes: { _inspect: [], data: null },
|
||||
message:
|
||||
'Invalid value 1 supplied to : Partial<{| query: Partial<{| _inspect: pipe(JSON, boolean) |}> |}>/query: Partial<{| _inspect: pipe(JSON, boolean) |}>/_inspect: pipe(JSON, boolean)',
|
||||
},
|
||||
|
|
|
@ -170,6 +170,7 @@ export function registerRoutes({
|
|||
body: {
|
||||
message: error.message,
|
||||
attributes: {
|
||||
data: {},
|
||||
_inspect: inspectableEsQueriesMap.get(request),
|
||||
},
|
||||
},
|
||||
|
@ -181,6 +182,7 @@ export function registerRoutes({
|
|||
|
||||
if (Boom.isBoom(error)) {
|
||||
opts.statusCode = error.output.statusCode;
|
||||
opts.body.attributes.data = error?.data;
|
||||
}
|
||||
|
||||
// capture error with APM node agent
|
||||
|
|
|
@ -53,6 +53,10 @@ import {
|
|||
import { InfraPluginStart, InfraPluginSetup } from '@kbn/infra-plugin/server';
|
||||
import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
||||
|
||||
import {
|
||||
CustomIntegrationsPluginSetup,
|
||||
CustomIntegrationsPluginStart,
|
||||
} from '@kbn/custom-integrations-plugin/server';
|
||||
import { APMConfig } from '.';
|
||||
import { ApmIndicesConfig } from './routes/settings/apm_indices/get_apm_indices';
|
||||
import { APMEventClient } from './lib/helpers/create_es_client/create_apm_event_client';
|
||||
|
@ -90,8 +94,8 @@ export interface APMPluginSetupDependencies {
|
|||
spaces?: SpacesPluginSetup;
|
||||
taskManager?: TaskManagerSetupContract;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
customIntegrations?: CustomIntegrationsPluginSetup;
|
||||
}
|
||||
|
||||
export interface APMPluginStartDependencies {
|
||||
// required dependencies
|
||||
data: DataPluginStart;
|
||||
|
@ -114,4 +118,5 @@ export interface APMPluginStartDependencies {
|
|||
spaces?: SpacesPluginStart;
|
||||
taskManager?: TaskManagerStartContract;
|
||||
usageCollection?: undefined;
|
||||
customIntegrations?: CustomIntegrationsPluginStart;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"@kbn/shared-ux-prompt-not-found",
|
||||
"@kbn/ui-actions-plugin",
|
||||
"@kbn/observability-alert-details",
|
||||
"@kbn/custom-integrations-plugin",
|
||||
"@kbn/dashboard-plugin",
|
||||
"@kbn/controls-plugin",
|
||||
"@kbn/core-http-server",
|
||||
|
|
|
@ -54,8 +54,14 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
const error = await expectToReject<ApmApiError>(() =>
|
||||
createAgentKey(apmApiClient.writeUser)
|
||||
);
|
||||
expect(error.res.status).to.be(500);
|
||||
expect(error.res.status).to.be(403);
|
||||
expect(error.res.body.message).contain('is missing the following requested privilege');
|
||||
expect(error.res.body.attributes).to.eql({
|
||||
_inspect: [],
|
||||
data: {
|
||||
missingPrivileges: allApplicationPrivileges,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error when invalidating an agent key', async () => {
|
||||
|
@ -79,7 +85,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
const error = await expectToReject<ApmApiError>(() =>
|
||||
createAgentKey(apmApiClient.manageOwnAgentKeysUser, [privilege])
|
||||
);
|
||||
expect(error.res.status).to.be(500);
|
||||
expect(error.res.status).to.be(403);
|
||||
expect(error.res.body.message).contain('is missing the following requested privilege');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue