From 0e3333d5fca9577313eb22d9a85c30f7d6d37afc Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Mon, 22 May 2023 17:04:38 +0200 Subject: [PATCH 01/34] Add first version of custom tutorials --- config/serverless.oblt.yml | 5 +- .../plugins/apm/common/tutorial/tutorials.ts | 32 ++ x-pack/plugins/apm/kibana.jsonc | 3 +- .../tutorials/agent_config_instructions.tsx | 71 +++ .../app/tutorials/agent_config_table.tsx | 54 +++ .../app/tutorials/commands/django.ts | 77 +++ .../app/tutorials/commands/dotnet.ts | 57 +++ .../app/tutorials/commands/flask.ts | 70 +++ .../commands/get_apm_agent_commands.test.ts | 457 ++++++++++++++++++ .../commands/get_apm_agent_commands.ts | 150 ++++++ .../components/app/tutorials/commands/go.ts | 63 +++ .../components/app/tutorials/commands/java.ts | 48 ++ .../components/app/tutorials/commands/node.ts | 64 +++ .../components/app/tutorials/commands/php.ts | 47 ++ .../components/app/tutorials/commands/rack.ts | 58 +++ .../app/tutorials/commands/rails.ts | 58 +++ .../app/tutorials/commands/shared_hints.ts | 48 ++ .../components/app/tutorials/footer.tsx | 54 +++ .../public/components/app/tutorials/index.tsx | 41 ++ .../app/tutorials/instruction_variants.ts | 36 ++ .../tutorials/instructions/django_agent.tsx | 78 +++ .../tutorials/instructions/dotnet_agent.tsx | 136 ++++++ .../tutorials/instructions/flask_agent.tsx | 78 +++ .../app/tutorials/instructions/go_agent.tsx | 113 +++++ .../app/tutorials/instructions/index.ts | 17 + .../app/tutorials/instructions/java_agent.tsx | 86 ++++ .../app/tutorials/instructions/node_agent.tsx | 78 +++ .../app/tutorials/instructions/otel_agent.tsx | 225 +++++++++ .../app/tutorials/instructions/php_agent.tsx | 113 +++++ .../app/tutorials/instructions/rack_agent.tsx | 125 +++++ .../tutorials/instructions/rails_agent.tsx | 77 +++ .../app/tutorials/instructions_set.tsx | 105 ++++ .../components/app/tutorials/introduction.tsx | 85 ++++ .../app/tutorials/serverless_instructions.tsx | 71 +++ .../components/routing/apm_route_config.tsx | 2 + .../components/routing/tutorials/index.tsx | 15 + x-pack/plugins/apm/server/index.ts | 2 + x-pack/plugins/apm/server/plugin.ts | 30 +- x-pack/plugins/apm/server/types.ts | 3 +- 39 files changed, 2920 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/apm/common/tutorial/tutorials.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/agent_config_instructions.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/agent_config_table.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/django.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/dotnet.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/flask.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.test.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/go.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/java.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/node.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/php.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/rack.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/rails.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/commands/shared_hints.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/footer.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/index.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instruction_variants.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/django_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/dotnet_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/flask_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/go_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/index.ts create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/java_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/node_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/otel_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/php_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/rack_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions/rails_agent.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/instructions_set.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/introduction.tsx create mode 100644 x-pack/plugins/apm/public/components/app/tutorials/serverless_instructions.tsx create mode 100644 x-pack/plugins/apm/public/components/routing/tutorials/index.tsx diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index 2c4de7a4addaf..d80b52f80ceee 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -5,7 +5,7 @@ enterpriseSearch.enabled: false xpack.cloudSecurityPosture.enabled: false xpack.securitySolution.enabled: false -## Enable the Serverless Obsersability plugin +## Enable the Serverless Observability plugin xpack.serverless.observability.enabled: true # Onboarding @@ -22,3 +22,6 @@ 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 diff --git a/x-pack/plugins/apm/common/tutorial/tutorials.ts b/x-pack/plugins/apm/common/tutorial/tutorials.ts new file mode 100644 index 0000000000000..128d5393821d2 --- /dev/null +++ b/x-pack/plugins/apm/common/tutorial/tutorials.ts @@ -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 = { + id: 'apm', + title: i18n.translate('xpack.apm.tutorial.specProvider.name', { + defaultMessage: 'APM', + }), + categories: APM_INTEGRATION_CATEGORIES, + uiInternalPath: '/app/apm/tutorials', + 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, +}; diff --git a/x-pack/plugins/apm/kibana.jsonc b/x-pack/plugins/apm/kibana.jsonc index 25f98076b135e..d62c5c2c3a0b3 100644 --- a/x-pack/plugins/apm/kibana.jsonc +++ b/x-pack/plugins/apm/kibana.jsonc @@ -42,7 +42,8 @@ "security", "spaces", "taskManager", - "usageCollection" + "usageCollection", + "customIntegrations", // Move this to requiredPlugins after completely migrating from the Tutorials Home App ], "requiredBundles": [ "fleet", diff --git a/x-pack/plugins/apm/public/components/app/tutorials/agent_config_instructions.tsx b/x-pack/plugins/apm/public/components/app/tutorials/agent_config_instructions.tsx new file mode 100644 index 0000000000000..508077bff132e --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/agent_config_instructions.tsx @@ -0,0 +1,71 @@ +/* + * 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, +}: { + variantId: string; + apmServerUrl?: string; + secretToken?: string; + apiKey?: string; +}) { + const defaultValues = { + apmServiceName: 'my-service-name', + apmEnvironment: 'my-environment', + }; + + const commands = getApmAgentCommands({ + variantId, + apmServerUrl, + secretToken, + apiKey, + defaultValues, + }); + + const variables = getApmAgentVariables(variantId); + const lineNumbers = getApmAgentLineNumbers(variantId, apiKey); + const highlightLang = getApmAgentHighlightLang(variantId); + + if (apiKey) { + delete variables.secretToken; + } else { + delete variables.apiKey; + } + + return ( + <> + + + + + + {commands} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/tutorials/agent_config_table.tsx b/x-pack/plugins/apm/public/components/app/tutorials/agent_config_table.tsx new file mode 100644 index 0000000000000..aa239c7229e0f --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/agent_config_table.tsx @@ -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 React from 'react'; +import type { ValuesType } from 'utility-types'; +import { get } from 'lodash'; +import { EuiBasicTable, EuiText, EuiBasicTableColumn } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export function AgentConfigurationTable({ + variables, + data, +}: { + variables: { [key: string]: string }; + data: { + apmServerUrl?: string; + secretToken?: string; + apiKey?: string; + apmServiceName: string; + apmEnvironment: string; + }; +}) { + if (!variables) return null; + + const columns: Array>> = [ + { + field: 'setting', + name: i18n.translate('xpack.apm.tutorial.agent.column.configSettings', { + defaultMessage: 'Configuration setting', + }), + }, + { + field: 'value', + name: i18n.translate('xpack.apm.tutorial.agent.column.configValue', { + defaultMessage: 'Configuration value', + }), + render: (_, { value }) => ( + + {value} + + ), + }, + ]; + + const items = Object.keys(variables).map((k) => ({ + setting: variables[k], + value: get(data, k), // TODO do we want default values? + })); + return ; +} diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/django.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/django.ts new file mode 100644 index 0000000000000..dc1bdf60009d7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/django.ts @@ -0,0 +1,77 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const djangoVariables = { + apmServiceName: 'SERVICE_NAME', + secretToken: 'SECRET_TOKEN', + apiKey: 'API_KEY', + apmServerUrl: 'SERVER_URL', + apmEnvironment: 'ENVIRONMENT', +}; + +export const djangoHighlightLang = 'py'; + +const djangoAddAgentComment = i18n.translate( + 'xpack.apm.tutorial.djangoClient.configure.commands.addAgentComment', + { + defaultMessage: 'Add the agent to installed apps', + } +); + +const djangoTracingMiddlewareComment = i18n.translate( + 'xpack.apm.tutorial.djangoClient.configure.commands.addTracingMiddlewareComment', + { + defaultMessage: 'Add our tracing middleware to send performance metrics', + } +); + +export const djangoLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '1-4, 7, 9, 11, 13, 16-19', + annotations: { + 2: djangoAddAgentComment, + 7: serviceNameHint, + 9: apiKey ? apiKeyHint : secretTokenHint, + 11: serverUrlHint, + 13: serviceEnvironmentHint, + 17: djangoTracingMiddlewareComment, + }, +}); + +export const django = `INSTALLED_APPS = ( + 'elasticapm.contrib.django', + # ... +) + +ELASTIC_APM = { + '${djangoVariables.apmServiceName}': '{{{apmServiceName}}}', + + {{#apiKey}} + '${djangoVariables.apiKey}': '{{{apiKey}}}', + {{/apiKey}} + {{^apiKey}} + '${djangoVariables.secretToken}': '{{{secretToken}}}', + {{/apiKey}} + + '${djangoVariables.apmServerUrl}': '{{{apmServerUrl}}}', + + '${djangoVariables.apmEnvironment}': '{{{apmEnvironment}}}', +} + +MIDDLEWARE = ( + 'elasticapm.contrib.django.middleware.TracingMiddleware', + #... +)`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/dotnet.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/dotnet.ts new file mode 100644 index 0000000000000..16d78686289c4 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/dotnet.ts @@ -0,0 +1,57 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const dotnetVariables = { + apmServiceName: 'ServiceName', + secretToken: 'SecretToken', + apiKey: 'ApiKey', + apmServerUrl: 'ServerUrl', + apmEnvironment: 'Environment', +}; + +export const dotnetHighlightLang = 'dotnet'; + +const dotnetServiceNameHint = i18n.translate( + 'xpack.apm.tutorial.dotnetClient.createConfig.commands.defaultServiceName', + { + defaultMessage: 'Default is the entry assembly of the application.', + } +); + +export const dotnetLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '3, 4, 5, 6', + annotations: { + 3: `${serviceNameHint} ${dotnetServiceNameHint}`, + 4: apiKey ? apiKeyHint : secretTokenHint, + 5: serverUrlHint, + 6: serviceEnvironmentHint, + }, +}); + +export const dotnet = `{ + "ElasticApm": { + "${dotnetVariables.apmServiceName}": "{{{apmServiceName}}}", + {{#apiKey}} + "${dotnetVariables.apiKey}": "{{{apiKey}}}", + {{/apiKey}} + {{^apiKey}} + "${dotnetVariables.secretToken}": "{{{secretToken}}}", + {{/apiKey}} + "${dotnetVariables.apmServerUrl}": "{{{apmServerUrl}}}", + "${dotnetVariables.apmEnvironment}": "{{{apmEnvironment}}}", + } +}`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/flask.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/flask.ts new file mode 100644 index 0000000000000..30df455e8a306 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/flask.ts @@ -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 { i18n } from '@kbn/i18n'; +import { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const flaskVariables = { + apmServiceName: 'SERVICE_NAME', + secretToken: 'SECRET_TOKEN', + apiKey: 'API_KEY', + apmServerUrl: 'SERVER_URL', + apmEnvironment: 'ENVIRONMENT', +}; + +export const flaskHighlightLang = 'py'; + +export const flaskLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '2-4, 7-18', + annotations: { + 9: serviceNameHint, + 11: apiKey ? apiKeyHint : secretTokenHint, + 13: serverUrlHint, + 15: serviceEnvironmentHint, + }, +}); + +export const flask = `# ${i18n.translate( + 'xpack.apm.tutorial.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.tutorial.flaskClient.configure.commands.configureElasticApmComment', + { + defaultMessage: "Or use ELASTIC_APM in your application's settings", + } +)} +from elasticapm.contrib.flask import ElasticAPM +app.config['ELASTIC_APM'] = { + '${flaskVariables.apmServiceName}': '{{{apmServiceName}}}', + + {{#apiKey}} + '${flaskVariables.apiKey}': '{{{apiKey}}}', + {{/apiKey}} + {{^apiKey}} + '${flaskVariables.secretToken}': '{{{secretToken}}}', + {{/apiKey}} + + '${flaskVariables.apmServerUrl}': '{{{apmServerUrl}}}', + + '${flaskVariables.apmEnvironment}': '{{{apmEnvironment}}}', +} + +apm = ElasticAPM(app)`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.test.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.test.ts new file mode 100644 index 0000000000000..d7ac4ab16e2e8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.test.ts @@ -0,0 +1,457 @@ +/* + * 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', () => { + const defaultValues = { + apmServiceName: 'my-service-name', + apmEnvironment: 'my-environment', + }; + describe('unknown agent', () => { + it('renders empty command', () => { + const commands = getApmAgentCommands({ + variantId: 'foo', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).toBe(''); + }); + }); + describe('java agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'java', + defaultValues, + }); + expect(commands).toMatchInlineSnapshot(` + "java -javaagent:/path/to/elastic-apm-agent-.jar \\\\ + -Delastic.apm.service_name=my-service-name \\\\ + -Delastic.apm.secret_token= \\\\ + -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', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "java -javaagent:/path/to/elastic-apm-agent-.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('RUM(js) agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'js', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "import { init as initApm } from '@elastic/apm-rum' + var apm = initApm({ + serviceName: 'my-service-name', + + serverUrl: '', + + serviceVersion: '', + + environment: 'my-environment' + })" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'js', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "import { init as initApm } from '@elastic/apm-rum' + var apm = initApm({ + serviceName: 'my-service-name', + + serverUrl: 'localhost:8220', + + serviceVersion: '', + + environment: 'my-environment' + })" + `); + }); + }); + describe('Node.js agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'node', + defaultValues, + }); + 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({ + serviceName: 'my-service-name', + + secretToken: '', + + serverUrl: '', + + environment: 'my-environment' + })" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'node', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + 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({ + serviceName: 'my-service-name', + + secretToken: 'foobar', + + serverUrl: 'localhost:8220', + + environment: 'my-environment' + })" + `); + }); + }); + describe('Django agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'django', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "INSTALLED_APPS = ( + 'elasticapm.contrib.django', + # ... + ) + + ELASTIC_APM = { + 'SERVICE_NAME': 'my-service-name', + + 'SECRET_TOKEN': '', + + 'SERVER_URL': '', + + 'ENVIRONMENT': 'my-environment', + } + + MIDDLEWARE = ( + 'elasticapm.contrib.django.middleware.TracingMiddleware', + #... + )" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'django', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "INSTALLED_APPS = ( + 'elasticapm.contrib.django', + # ... + ) + + ELASTIC_APM = { + 'SERVICE_NAME': 'my-service-name', + + 'SECRET_TOKEN': 'foobar', + + 'SERVER_URL': 'localhost:8220', + + 'ENVIRONMENT': 'my-environment', + } + + MIDDLEWARE = ( + 'elasticapm.contrib.django.middleware.TracingMiddleware', + #... + )" + `); + }); + }); + describe('Flask agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'flask', + defaultValues, + }); + 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'] = { + 'SERVICE_NAME': 'my-service-name', + + 'SECRET_TOKEN': '', + + 'SERVER_URL': '', + + 'ENVIRONMENT': 'my-environment', + } + + apm = ElasticAPM(app)" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'flask', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + 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'] = { + 'SERVICE_NAME': 'my-service-name', + + 'SECRET_TOKEN': 'foobar', + + 'SERVER_URL': 'localhost:8220', + + 'ENVIRONMENT': 'my-environment', + } + + apm = ElasticAPM(app)" + `); + }); + }); + describe('Ruby on Rails agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'rails', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# config/elastic_apm.yml: + + service_name: 'my-service-name' + + secret_token: '' + + server_url: '' + + environment: 'my-environment'" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'rails', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# config/elastic_apm.yml: + + service_name: 'my-service-name' + + secret_token: 'foobar' + + server_url: 'localhost:8220' + + environment: 'my-environment'" + `); + }); + }); + describe('Rack agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'rack', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# config/elastic_apm.yml: + + service_name: 'my-service-name' + + secret_token: '' + + server_url: '', + + environment: 'my-environment'" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'rack', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# config/elastic_apm.yml: + + service_name: 'my-service-name' + + secret_token: 'foobar' + + server_url: 'localhost:8220', + + environment: 'my-environment'" + `); + }); + }); + describe('Go agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'go', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# Initialize using environment variables: + export ELASTIC_APM_SERVICE_NAME=my-service-name + + export ELASTIC_APM_SECRET_TOKEN= + + export ELASTIC_APM_SERVER_URL= + + export ELASTIC_APM_ENVIRONMENT=my-environment + " + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'go', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "# Initialize using environment variables: + export ELASTIC_APM_SERVICE_NAME=my-service-name + + export ELASTIC_APM_SECRET_TOKEN=foobar + + export ELASTIC_APM_SERVER_URL=localhost:8220 + + export ELASTIC_APM_ENVIRONMENT=my-environment + " + `); + }); + }); + describe('dotNet agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'dotnet', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "{ + \\"ElasticApm\\": { + \\"ServiceName\\": \\"my-service-name\\", + \\"SecretToken\\": \\"\\", + \\"ServerUrl\\": \\"\\", + \\"Environment\\": \\"my-environment\\", + } + }" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'dotnet', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "{ + \\"ElasticApm\\": { + \\"ServiceName\\": \\"my-service-name\\", + \\"SecretToken\\": \\"foobar\\", + \\"ServerUrl\\": \\"localhost:8220\\", + \\"Environment\\": \\"my-environment\\", + } + }" + `); + }); + }); + describe('PHP agent', () => { + it('renders empty commands', () => { + const commands = getApmAgentCommands({ + variantId: 'php', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "elastic_apm.service_name=\\"my-service-name\\" + + elastic_apm.secret_token=\\"\\" + + elastic_apm.server_url=\\"\\" + + elastic_apm.environment=\\"my-environment\\"" + `); + }); + it('renders with secret token and url', () => { + const commands = getApmAgentCommands({ + variantId: 'php', + apmServerUrl: 'localhost:8220', + secretToken: 'foobar', + defaultValues, + }); + expect(commands).not.toBe(''); + expect(commands).toMatchInlineSnapshot(` + "elastic_apm.service_name=\\"my-service-name\\" + + elastic_apm.secret_token=\\"foobar\\" + + elastic_apm.server_url=\\"localhost:8220\\" + + elastic_apm.environment=\\"my-environment\\"" + `); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.ts new file mode 100644 index 0000000000000..71306c537e5a7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/get_apm_agent_commands.ts @@ -0,0 +1,150 @@ +/* + * 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'; + +const apmAgentCommandsMap: Record = { + java, + node, + django, + flask, + rails, + rack, + go, + dotnet, + php, +}; + +interface Variables { + [key: string]: string; +} + +const apmAgentVariablesMap: Record = { + java: javaVariables, + node: nodeVariables, + django: djangoVariables, + flask: flaskVariables, + rails: railsVariables, + rack: rackVariables, + go: goVariables, + dotnet: dotnetVariables, + php: phpVariables, +}; + +interface LineNumbers { + [key: string]: string | number | object; +} + +const apmAgentLineNumbersMap: ( + apiKey?: string +) => Record = (apiKey?: string) => ({ + java: javaLineNumbers(apiKey), + node: nodeLineNumbers(apiKey), + django: djangoLineNumbers(apiKey), + flask: flaskLineNumbers(apiKey), + rails: railsLineNumbers(apiKey), + rack: rackLineNumbers(apiKey), + go: goLineNumbers(apiKey), + dotnet: dotnetLineNumbers(apiKey), + php: phpLineNumbers(apiKey), +}); + +const apmAgentHighlightLangMap: Record = { + 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, + defaultValues, +}: { + variantId: string; + apmServerUrl?: string; + secretToken?: string; + apiKey?: string; + defaultValues: { + apmServiceName: string; + apmEnvironment: string; + }; +}) { + const commands = apmAgentCommandsMap[variantId]; + if (!commands) { + return ''; + } + + return Mustache.render(commands, { + apmServerUrl, + secretToken, + apiKey, + ...defaultValues, + }); +} + +export function getApmAgentVariables(variantId: string) { + return apmAgentVariablesMap[variantId]; +} + +export function getApmAgentLineNumbers(variantId: string, apiKey?: string) { + return apmAgentLineNumbersMap(apiKey)[variantId]; +} + +export function getApmAgentHighlightLang(variantId: string) { + return apmAgentHighlightLangMap[variantId]; +} diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/go.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/go.ts new file mode 100644 index 0000000000000..0e338142cb861 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/go.ts @@ -0,0 +1,63 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const goVariables = { + apmServiceName: 'ELASTIC_APM_SERVICE_NAME', + secretToken: 'ELASTIC_APM_SECRET_TOKEN', + apiKey: 'ELASTIC_APM_API_KEY', + apmServerUrl: 'ELASTIC_APM_SERVER_URL', + apmEnvironment: 'ELASTIC_APM_ENVIRONMENT', +}; + +export const goHighlightLang = 'go'; + +const goServiceNameHint = i18n.translate( + 'xpack.apm.tutorial.goClient.configure.commands.usedExecutableNameComment', + { + defaultMessage: 'If not specified, the executable name will be used.', + } +); + +export const goLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '2, 4, 6, 8', + annotations: { + 2: `${serviceNameHint} ${goServiceNameHint}`, + 4: apiKey ? apiKeyHint : secretTokenHint, + 6: serverUrlHint, + 8: serviceEnvironmentHint, + }, +}); + +export const go = `# ${i18n.translate( + 'xpack.apm.tutorial.goClient.configure.commands.initializeUsingEnvironmentVariablesComment', + { + defaultMessage: 'Initialize using environment variables:', + } +)} +export ${goVariables.apmServiceName}={{{apmServiceName}}} + +{{#apiKey}} +export ${goVariables.apiKey}={{{apiKey}}} +{{/apiKey}} +{{^apiKey}} +export ${goVariables.secretToken}={{{secretToken}}} +{{/apiKey}} + +export ${goVariables.apmServerUrl}={{{apmServerUrl}}} + +export ${goVariables.apmEnvironment}={{{apmEnvironment}}} +`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/java.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/java.ts new file mode 100644 index 0000000000000..de65aa6dafbe5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/java.ts @@ -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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const javaVariables = { + apmServiceName: 'Delastic.apm.service_name', + secretToken: 'Delastic.apm.secret_token', + apiKey: 'Delastic.apm.api_key', + apmServerUrl: 'Delastic.apm.server_url', + apmEnvironment: 'Delastic.apm.environment', +}; + +export const javaHighlightLang = 'java'; + +export const javaLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '', + annotations: { + 2: serviceNameHint, + 3: apiKey ? apiKeyHint : secretTokenHint, + 4: serverUrlHint, + 5: serviceEnvironmentHint, + }, +}); + +export const java = `java -javaagent:/path/to/elastic-apm-agent-.jar \\ +-${javaVariables.apmServiceName}={{{apmServiceName}}} \\ +{{#apiKey}} +-${javaVariables.apiKey}={{{apiKey}}} \\ +{{/apiKey}} +{{^apiKey}} +-${javaVariables.secretToken}={{{secretToken}}} \\ +{{/apiKey}} +-${javaVariables.apmServerUrl}={{{apmServerUrl}}} \\ +-${javaVariables.apmEnvironment}={{{apmEnvironment}}} \\ +-Delastic.apm.application_packages=org.example \\ +-jar {{{apmServiceName}}}.jar;`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/node.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/node.ts new file mode 100644 index 0000000000000..ff27805feb310 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/node.ts @@ -0,0 +1,64 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const nodeVariables = { + apmServiceName: 'serviceName', + secretToken: 'secretToken', + apiKey: 'apiKey', + apmServerUrl: 'serverUrl', + apmEnvironment: 'environment', +}; + +export const nodeHighlightLang = 'js'; + +const nodeServiceNameHint = i18n.translate( + 'xpack.apm.tutorial.nodeClient.createConfig.commands.serviceName', + { + defaultMessage: 'Overrides the service name in package.json.', + } +); + +export const nodeLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '3, 5, 7, 9', + annotations: { + 3: `${serviceNameHint} ${nodeServiceNameHint}`, + 5: apiKey ? apiKeyHint : secretTokenHint, + 7: serverUrlHint, + 9: serviceEnvironmentHint, + }, +}); + +export const node = `// ${i18n.translate( + 'xpack.apm.tutorial.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({ + ${nodeVariables.apmServiceName}: '{{{apmServiceName}}}', + + {{#apiKey}} + ${nodeVariables.apiKey}: '{{{apiKey}}}', + {{/apiKey}} + {{^apiKey}} + ${nodeVariables.secretToken}: '{{{secretToken}}}', + {{/apiKey}} + + ${nodeVariables.apmServerUrl}: '{{{apmServerUrl}}}', + + ${nodeVariables.apmEnvironment}: '{{{apmEnvironment}}}' +})`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/php.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/php.ts new file mode 100644 index 0000000000000..92631eef77e59 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/php.ts @@ -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 phpVariables = { + apmServiceName: 'elastic_apm.service_name', + secretToken: 'elastic_apm.secret_token', + apiKey: 'elastic_apm.api_key', + apmServerUrl: 'elastic_apm.server_url', + apmEnvironment: 'elastic_apm.environment', +}; + +export const phpHighlightLang = 'php'; + +export const phpLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '1, 3, 5, 7', + annotations: { + 1: serviceNameHint, + 3: apiKey ? apiKeyHint : secretTokenHint, + 5: serverUrlHint, + 7: serviceEnvironmentHint, + }, +}); + +export const php = `${phpVariables.apmServiceName}="{{{apmServiceName}}}" + +{{#apiKey}} +${phpVariables.apiKey}="{{{apiKey}}}" +{{/apiKey}} +{{^apiKey}} +${phpVariables.secretToken}="{{{secretToken}}}" +{{/apiKey}} + +${phpVariables.apmServerUrl}="{{{apmServerUrl}}}" + +${phpVariables.apmEnvironment}="{{{apmEnvironment}}}"`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/rack.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/rack.ts new file mode 100644 index 0000000000000..8968bed251dd9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/rack.ts @@ -0,0 +1,58 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const rackVariables = { + apmServiceName: 'service_name', + secretToken: 'secret_token', + apiKey: 'api_key', + apmServerUrl: 'server_url', + apmEnvironment: 'environment', +}; + +export const rackHighlightLang = 'rb'; + +const rackServiceNameHint = i18n.translate( + 'xpack.apm.tutorial.rackClient.createConfig.commands.defaultsToTheNameOfRackAppClassComment', + { + defaultMessage: "Defaults to the name of your Rack app's class.", + } +); + +export const rackLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '3, 5, 7, 9', + annotations: { + 3: `${serviceNameHint} ${rackServiceNameHint}`, + 5: apiKey ? apiKeyHint : secretTokenHint, + 7: serverUrlHint, + 9: serviceEnvironmentHint, + }, +}); + +export const rack = `# config/elastic_apm.yml: + +${rackVariables.apmServiceName}: '{{{apmServiceName}}}' + +{{#apiKey}} +${rackVariables.apiKey}: '{{{apiKey}}}', +{{/apiKey}} +{{^apiKey}} +${rackVariables.secretToken}: '{{{secretToken}}}' +{{/apiKey}} + +${rackVariables.apmServerUrl}: '{{{apmServerUrl}}}', + +${rackVariables.apmEnvironment}: '{{{apmEnvironment}}}'`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/rails.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/rails.ts new file mode 100644 index 0000000000000..2bc9ffb132d5c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/rails.ts @@ -0,0 +1,58 @@ +/* + * 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 { + serviceNameHint, + secretTokenHint, + serverUrlHint, + serviceEnvironmentHint, + apiKeyHint, +} from './shared_hints'; + +export const railsVariables = { + apmServiceName: 'service_name', + secretToken: 'secret_token', + apiKey: 'api_key', + apmServerUrl: 'server_url', + apmEnvironment: 'environment', +}; + +export const railsHighlightLang = 'rb'; + +const railsServiceNameHint = i18n.translate( + 'xpack.apm.tutorial.railsClient.createConfig.commands.defaultServiceName', + { + defaultMessage: 'Defaults to the name of your Rails app.', + } +); + +export const railsLineNumbers = (apiKey?: string) => ({ + start: 1, + highlight: '3, 5, 7, 9', + annotations: { + 3: `${serviceNameHint} ${railsServiceNameHint}`, + 5: apiKey ? apiKeyHint : secretTokenHint, + 7: serverUrlHint, + 9: serviceEnvironmentHint, + }, +}); + +export const rails = `# config/elastic_apm.yml: + +${railsVariables.apmServiceName}: '{{{apmServiceName}}}' + +{{#apiKey}} +${railsVariables.apiKey}: '{{{apiKey}}}', +{{/apiKey}} +{{^apiKey}} +${railsVariables.secretToken}: '{{{secretToken}}}' +{{/apiKey}} + +${railsVariables.apmServerUrl}: '{{{apmServerUrl}}}' + +${railsVariables.apmEnvironment}: '{{{apmEnvironment}}}'`; diff --git a/x-pack/plugins/apm/public/components/app/tutorials/commands/shared_hints.ts b/x-pack/plugins/apm/public/components/app/tutorials/commands/shared_hints.ts new file mode 100644 index 0000000000000..06e3f80cbbae4 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/commands/shared_hints.ts @@ -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 serviceNameHint = i18n.translate( + 'xpack.apm.tutorial.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.tutorial.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.tutorial.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.\n' + + '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.tutorial.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.tutorial.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.`, + } +); diff --git a/x-pack/plugins/apm/public/components/app/tutorials/footer.tsx b/x-pack/plugins/apm/public/components/app/tutorials/footer.tsx new file mode 100644 index 0000000000000..2a32b8f327828 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/footer.tsx @@ -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 { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + 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 ( + <> + + + + + +

+ +

+
+
+ + + + {i18n.translate('xpack.apm.tutorial.footer.cta', { + defaultMessage: 'Launch APM', + })} + + +
+
+ + ); +} diff --git a/x-pack/plugins/apm/public/components/app/tutorials/index.tsx b/x-pack/plugins/apm/public/components/app/tutorials/index.tsx new file mode 100644 index 0000000000000..59a5c74b0589f --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/tutorials/index.tsx @@ -0,0 +1,41 @@ +/* + * 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 { useKibana } from '@kbn/kibana-react-plugin/public'; +import { EuiSpacer } from '@elastic/eui'; +import { ApmPluginStartDeps } from '../../../plugin'; +import { Introduction } from './introduction'; +import { InstructionsSet } from './instructions_set'; +import { serverlessInstructions } from './serverless_instructions'; +import { Footer } from './footer'; + +export function Tutorials() { + const { services } = useKibana(); + 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 commonOptions = { + baseUrl, + }; + + const serverless = serverlessInstructions(commonOptions); + + const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate; + return ( + + + + +