Files
oneuptime/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx

1901 lines
58 KiB
TypeScript

import React, {
FunctionComponent,
ReactElement,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import CodeBlock from "Common/UI/Components/CodeBlock/CodeBlock";
import TelemetryIngestionKey from "Common/Models/DatabaseModels/TelemetryIngestionKey";
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
import ProjectUtil from "Common/UI/Utils/Project";
import { HOST, HTTP_PROTOCOL } from "Common/UI/Config";
import ModelFormModal from "Common/UI/Components/ModelFormModal/ModelFormModal";
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
import { FormType } from "Common/UI/Components/Forms/ModelForm";
import API from "Common/UI/Utils/API/API";
import IconProp from "Common/Types/Icon/IconProp";
import Icon from "Common/UI/Components/Icon/Icon";
import Dropdown, {
DropdownOption,
DropdownValue,
} from "Common/UI/Components/Dropdown/Dropdown";
import Protocol from "Common/Types/API/Protocol";
export type TelemetryType =
| "logs"
| "metrics"
| "traces"
| "exceptions"
| "profiles";
export interface ComponentProps {
telemetryType?: TelemetryType | undefined;
onClose?: (() => void) | undefined;
}
type Language =
| "node"
| "python"
| "go"
| "java"
| "dotnet"
| "rust"
| "php"
| "ruby"
| "elixir"
| "cpp"
| "swift"
| "react"
| "angular";
interface LanguageOption {
key: Language;
label: string;
shortLabel: string;
}
const languages: Array<LanguageOption> = [
{ key: "node", label: "Node.js / TypeScript", shortLabel: "Node.js" },
{ key: "python", label: "Python", shortLabel: "Python" },
{ key: "go", label: "Go", shortLabel: "Go" },
{ key: "java", label: "Java", shortLabel: "Java" },
{ key: "dotnet", label: ".NET / C#", shortLabel: ".NET" },
{ key: "rust", label: "Rust", shortLabel: "Rust" },
{ key: "php", label: "PHP", shortLabel: "PHP" },
{ key: "ruby", label: "Ruby", shortLabel: "Ruby" },
{ key: "elixir", label: "Elixir", shortLabel: "Elixir" },
{ key: "cpp", label: "C++", shortLabel: "C++" },
{ key: "swift", label: "Swift", shortLabel: "Swift" },
{ key: "react", label: "React (Browser)", shortLabel: "React" },
{ key: "angular", label: "Angular (Browser)", shortLabel: "Angular" },
];
type IntegrationMethod = "opentelemetry" | "fluentbit" | "fluentd" | "alloy";
interface IntegrationOption {
key: IntegrationMethod;
label: string;
description: string;
}
// Helper to replace placeholders in code snippets
function replacePlaceholders(
code: string,
otlpUrl: string,
otlpHost: string,
token: string,
pyroscopeUrl: string,
): string {
return code
.replace(/<YOUR_ONEUPTIME_URL>/g, otlpUrl)
.replace(/<YOUR_ONEUPTIME_PYROSCOPE_URL>/g, pyroscopeUrl)
.replace(/<YOUR_ONEUPTIME_OTLP_HOST>/g, otlpHost)
.replace(/<YOUR_ONEUPTIME_TOKEN>/g, token);
}
// --- OpenTelemetry code snippets per language ---
function getOtelInstallSnippet(lang: Language): {
code: string;
language: string;
} {
switch (lang) {
case "node":
return {
code: `npm install @opentelemetry/sdk-node \\
@opentelemetry/auto-instrumentations-node \\
@opentelemetry/exporter-trace-otlp-proto \\
@opentelemetry/exporter-metrics-otlp-proto \\
@opentelemetry/exporter-logs-otlp-proto`,
language: "bash",
};
case "python":
return {
code: `pip install opentelemetry-api \\
opentelemetry-sdk \\
opentelemetry-exporter-otlp-proto-http \\
opentelemetry-instrumentation`,
language: "bash",
};
case "go":
return {
code: `go get go.opentelemetry.io/otel \\
go.opentelemetry.io/otel/sdk \\
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \\
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp \\
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`,
language: "bash",
};
case "java":
return {
code: `# Download the OpenTelemetry Java Agent
curl -L -o opentelemetry-javaagent.jar \\
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar`,
language: "bash",
};
case "dotnet":
return {
code: `dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http`,
language: "bash",
};
case "rust":
return {
code: `# Add to Cargo.toml
[dependencies]
opentelemetry = "0.22"
opentelemetry_sdk = { version = "0.22", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.15", features = ["http-proto", "reqwest-client"] }
tracing = "0.1"
tracing-opentelemetry = "0.23"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
reqwest = { version = "0.11", features = ["blocking"] }`,
language: "bash",
};
case "php":
return {
code: `composer require open-telemetry/sdk \\
open-telemetry/exporter-otlp \\
open-telemetry/transport-http`,
language: "bash",
};
case "ruby":
return {
code: `# Add to Gemfile
gem 'opentelemetry-sdk'
gem 'opentelemetry-exporter-otlp'
gem 'opentelemetry-instrumentation-all'
# Then run:
bundle install`,
language: "bash",
};
case "elixir":
return {
code: `# Add to mix.exs deps
defp deps do
[
{:opentelemetry, "~> 1.4"},
{:opentelemetry_sdk, "~> 1.4"},
{:opentelemetry_exporter, "~> 1.7"},
{:opentelemetry_phoenix, "~> 1.2"},
{:opentelemetry_ecto, "~> 1.2"}
]
end
# Then run:
mix deps.get`,
language: "bash",
};
case "cpp":
return {
code: `# Using vcpkg
vcpkg install opentelemetry-cpp[otlp-http]
# Or using CMake FetchContent:
# include(FetchContent)
# FetchContent_Declare(opentelemetry-cpp
# GIT_REPOSITORY https://github.com/open-telemetry/opentelemetry-cpp.git
# GIT_TAG v1.14.0)
# set(WITH_OTLP_HTTP ON)
# FetchContent_MakeAvailable(opentelemetry-cpp)`,
language: "bash",
};
case "swift":
return {
code: `// Add to Package.swift dependencies:
.package(url: "https://github.com/open-telemetry/opentelemetry-swift.git", from: "1.9.0")
// And add to target dependencies:
.product(name: "OpenTelemetryApi", package: "opentelemetry-swift"),
.product(name: "OpenTelemetrySdk", package: "opentelemetry-swift"),
.product(name: "OtlpHttpSpanExporting", package: "opentelemetry-swift"),`,
language: "bash",
};
case "react":
return {
code: `npm install @opentelemetry/api \\
@opentelemetry/sdk-trace-web \\
@opentelemetry/sdk-trace-base \\
@opentelemetry/exporter-trace-otlp-http \\
@opentelemetry/instrumentation-document-load \\
@opentelemetry/instrumentation-fetch \\
@opentelemetry/instrumentation-xml-http-request \\
@opentelemetry/context-zone`,
language: "bash",
};
case "angular":
return {
code: `npm install @opentelemetry/api \\
@opentelemetry/sdk-trace-web \\
@opentelemetry/sdk-trace-base \\
@opentelemetry/exporter-trace-otlp-http \\
@opentelemetry/instrumentation-document-load \\
@opentelemetry/instrumentation-fetch \\
@opentelemetry/instrumentation-xml-http-request \\
@opentelemetry/context-zone`,
language: "bash",
};
}
}
function getOtelConfigSnippet(lang: Language): {
code: string;
language: string;
} {
switch (lang) {
case "node":
return {
code: `// tracing.ts - Run with: node --require ./tracing.ts app.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
const sdk = new NodeSDK({
serviceName: 'my-service',
traceExporter: new OTLPTraceExporter({
url: '<YOUR_ONEUPTIME_URL>/v1/traces',
headers: { 'x-oneuptime-token': '<YOUR_ONEUPTIME_TOKEN>' },
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: '<YOUR_ONEUPTIME_URL>/v1/metrics',
headers: { 'x-oneuptime-token': '<YOUR_ONEUPTIME_TOKEN>' },
}),
}),
logRecordProcessors: [
new BatchLogRecordProcessor(
new OTLPLogExporter({
url: '<YOUR_ONEUPTIME_URL>/v1/logs',
headers: { 'x-oneuptime-token': '<YOUR_ONEUPTIME_TOKEN>' },
})
),
],
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();`,
language: "typescript",
};
case "python":
return {
code: `# tracing.py
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource
resource = Resource.create({"service.name": "my-service"})
# Traces
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint="<YOUR_ONEUPTIME_URL>",
headers={"x-oneuptime-token": "<YOUR_ONEUPTIME_TOKEN>"},
)
)
)
trace.set_tracer_provider(trace_provider)
# Metrics
metric_reader = PeriodicExportingMetricReader(
OTLPMetricExporter(
endpoint="<YOUR_ONEUPTIME_URL>",
headers={"x-oneuptime-token": "<YOUR_ONEUPTIME_TOKEN>"},
)
)
metrics.set_meter_provider(MeterProvider(resource=resource, metric_readers=[metric_reader]))`,
language: "python",
};
case "go":
return {
code: `package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)
func initTracer() (*sdktrace.TracerProvider, error) {
ctx := context.Background()
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint("<YOUR_ONEUPTIME_OTLP_HOST>"),
otlptracehttp.WithHeaders(map[string]string{
"x-oneuptime-token": "<YOUR_ONEUPTIME_TOKEN>",
}),
)
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("my-service"),
)),
)
otel.SetTracerProvider(tp)
return tp, nil
}`,
language: "go",
};
case "java":
return {
code: `# Run your Java application with the OpenTelemetry agent:
java -javaagent:opentelemetry-javaagent.jar \\
-Dotel.service.name=my-service \\
-Dotel.exporter.otlp.endpoint=<YOUR_ONEUPTIME_URL> \\
-Dotel.exporter.otlp.headers="x-oneuptime-token=<YOUR_ONEUPTIME_TOKEN>" \\
-Dotel.exporter.otlp.protocol=http/protobuf \\
-Dotel.metrics.exporter=otlp \\
-Dotel.logs.exporter=otlp \\
-jar my-app.jar`,
language: "bash",
};
case "dotnet":
return {
code: `// Program.cs
using OpenTelemetry;
using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Exporter;
var builder = WebApplication.CreateBuilder(args);
var resourceBuilder = ResourceBuilder.CreateDefault()
.AddService("my-service");
// Traces
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.SetResourceBuilder(resourceBuilder)
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(options => {
options.Endpoint = new Uri("<YOUR_ONEUPTIME_URL>");
options.Headers = "x-oneuptime-token=<YOUR_ONEUPTIME_TOKEN>";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
})
)
.WithMetrics(metrics => metrics
.SetResourceBuilder(resourceBuilder)
.AddAspNetCoreInstrumentation()
.AddOtlpExporter(options => {
options.Endpoint = new Uri("<YOUR_ONEUPTIME_URL>");
options.Headers = "x-oneuptime-token=<YOUR_ONEUPTIME_TOKEN>";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
})
);
// Logs
builder.Logging.AddOpenTelemetry(logging => {
logging.SetResourceBuilder(resourceBuilder);
logging.AddOtlpExporter(options => {
options.Endpoint = new Uri("<YOUR_ONEUPTIME_URL>");
options.Headers = "x-oneuptime-token=<YOUR_ONEUPTIME_TOKEN>";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
});
});
var app = builder.Build();
app.Run();`,
language: "csharp",
};
case "rust":
return {
code: `use opentelemetry::global;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::{trace as sdktrace, Resource};
use opentelemetry::KeyValue;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use std::collections::HashMap;
fn init_tracer() -> sdktrace::TracerProvider {
let mut headers = HashMap::new();
headers.insert(
"x-oneuptime-token".to_string(),
"<YOUR_ONEUPTIME_TOKEN>".to_string(),
);
let exporter = opentelemetry_otlp::new_exporter()
.http()
.with_endpoint("<YOUR_ONEUPTIME_URL>")
.with_headers(headers);
opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(exporter)
.with_trace_config(
sdktrace::Config::default()
.with_resource(Resource::new(vec![
KeyValue::new("service.name", "my-service"),
])),
)
.install_batch(opentelemetry_sdk::runtime::Tokio)
.unwrap()
}`,
language: "rust",
};
case "php":
return {
code: `<?php
// bootstrap.php
use OpenTelemetry\\API\\Globals;
use OpenTelemetry\\SDK\\Trace\\TracerProviderBuilder;
use OpenTelemetry\\SDK\\Trace\\SpanProcessor\\BatchSpanProcessor;
use OpenTelemetry\\Contrib\\Otlp\\SpanExporter;
use OpenTelemetry\\SDK\\Common\\Attribute\\Attributes;
use OpenTelemetry\\SDK\\Resource\\ResourceInfo;
use OpenTelemetry\\SemConv\\ResourceAttributes;
use OpenTelemetry\\Contrib\\Otlp\\HttpTransportFactory;
$transport = (new HttpTransportFactory())->create(
'<YOUR_ONEUPTIME_URL>/v1/traces',
'application/x-protobuf',
['x-oneuptime-token' => '<YOUR_ONEUPTIME_TOKEN>']
);
$exporter = new SpanExporter($transport);
$resource = ResourceInfo::create(Attributes::create([
ResourceAttributes::SERVICE_NAME => 'my-service',
]));
$tracerProvider = (new TracerProviderBuilder())
->addSpanProcessor(new BatchSpanProcessor($exporter))
->setResource($resource)
->build();
Globals::registerTracerProvider($tracerProvider);`,
language: "php",
};
case "ruby":
return {
code: `# config/initializers/opentelemetry.rb
require 'opentelemetry/sdk'
require 'opentelemetry/exporter/otlp'
require 'opentelemetry/instrumentation/all'
OpenTelemetry::SDK.configure do |c|
c.service_name = 'my-service'
c.add_span_processor(
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
OpenTelemetry::Exporter::OTLP::Exporter.new(
endpoint: '<YOUR_ONEUPTIME_URL>/v1/traces',
headers: { 'x-oneuptime-token' => '<YOUR_ONEUPTIME_TOKEN>' }
)
)
)
c.use_all # Auto-instrument all available libraries
end`,
language: "ruby",
};
case "elixir":
return {
code: `# config/runtime.exs
config :opentelemetry,
resource: %{service: %{name: "my-service"}},
span_processor: :batch,
traces_exporter: :otlp
config :opentelemetry_exporter,
otlp_protocol: :http_protobuf,
otlp_endpoint: "<YOUR_ONEUPTIME_URL>",
otlp_headers: [{"x-oneuptime-token", "<YOUR_ONEUPTIME_TOKEN>"}]
# In application.ex, add to children:
# {OpentelemetryPhoenix, []},
# {OpentelemetryEcto, repo: MyApp.Repo}`,
language: "elixir",
};
case "cpp":
return {
code: `#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
#include <opentelemetry/sdk/trace/batch_span_processor_factory.h>
#include <opentelemetry/sdk/resource/resource.h>
#include <opentelemetry/trace/provider.h>
namespace trace = opentelemetry::trace;
namespace sdktrace = opentelemetry::sdk::trace;
namespace otlp = opentelemetry::exporter::otlp;
void initTracer() {
otlp::OtlpHttpExporterOptions opts;
opts.url = "<YOUR_ONEUPTIME_URL>/v1/traces";
opts.http_headers = {{"x-oneuptime-token", "<YOUR_ONEUPTIME_TOKEN>"}};
auto exporter = otlp::OtlpHttpExporterFactory::Create(opts);
sdktrace::BatchSpanProcessorOptions bspOpts;
auto processor = sdktrace::BatchSpanProcessorFactory::Create(
std::move(exporter), bspOpts);
auto resource = opentelemetry::sdk::resource::Resource::Create({
{"service.name", "my-service"}
});
auto provider = sdktrace::TracerProviderFactory::Create(
std::move(processor), resource);
trace::Provider::SetTracerProvider(std::move(provider));
}`,
language: "cpp",
};
case "swift":
return {
code: `import OpenTelemetryApi
import OpenTelemetrySdk
import OtlpHttpSpanExporting
func initTracer() {
let exporter = OtlpHttpSpanExporter(
endpoint: URL(string: "<YOUR_ONEUPTIME_URL>/v1/traces")!,
config: OtlpConfiguration(
headers: [("x-oneuptime-token", "<YOUR_ONEUPTIME_TOKEN>")]
)
)
let spanProcessor = BatchSpanProcessor(spanExporter: exporter)
let tracerProvider = TracerProviderBuilder()
.add(spanProcessor: spanProcessor)
.with(resource: Resource(attributes: [
ResourceAttributes.serviceName.rawValue: AttributeValue.string("my-service")
]))
.build()
OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)
}`,
language: "swift",
};
case "react":
return {
code: `// src/tracing.ts - Import this file in your index.tsx before ReactDOM.render()
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { Resource } from '@opentelemetry/resources';
const provider = new WebTracerProvider({
resource: new Resource({
'service.name': 'my-react-app',
}),
});
provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: '<YOUR_ONEUPTIME_URL>/v1/traces',
headers: { 'x-oneuptime-token': '<YOUR_ONEUPTIME_TOKEN>' },
})
)
);
provider.register({
contextManager: new ZoneContextManager(),
});
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/.*/],
}),
new XMLHttpRequestInstrumentation({
propagateTraceHeaderCorsUrls: [/.*/],
}),
],
});
// In index.tsx:
// import './tracing'; // Must be first import
// import React from 'react';
// ...`,
language: "typescript",
};
case "angular":
return {
code: `// src/tracing.ts
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { Resource } from '@opentelemetry/resources';
const provider = new WebTracerProvider({
resource: new Resource({
'service.name': 'my-angular-app',
}),
});
provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: '<YOUR_ONEUPTIME_URL>/v1/traces',
headers: { 'x-oneuptime-token': '<YOUR_ONEUPTIME_TOKEN>' },
})
)
);
provider.register({
contextManager: new ZoneContextManager(),
});
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/.*/],
}),
new XMLHttpRequestInstrumentation({
propagateTraceHeaderCorsUrls: [/.*/],
}),
],
});
// In main.ts, import before bootstrapping:
// import './tracing'; // Must be first import
// import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
// ...`,
language: "typescript",
};
}
}
function getEnvVarSnippet(): string {
return `# Alternatively, configure via environment variables (works with any language):
export OTEL_SERVICE_NAME="my-service"
export OTEL_EXPORTER_OTLP_ENDPOINT="<YOUR_ONEUPTIME_URL>"
export OTEL_EXPORTER_OTLP_HEADERS="x-oneuptime-token=<YOUR_ONEUPTIME_TOKEN>"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"`;
}
// --- Profile-specific snippets ---
const profileLanguages: Array<Language> = [
"node",
"python",
"go",
"java",
"dotnet",
"ruby",
"rust",
];
function getProfileInstallSnippet(lang: Language): {
code: string;
language: string;
} {
switch (lang) {
case "node":
return {
code: `npm install @pyroscope/nodejs`,
language: "bash",
};
case "python":
return {
code: `pip install pyroscope-io`,
language: "bash",
};
case "go":
return {
code: `go get github.com/grafana/pyroscope-go`,
language: "bash",
};
case "java":
return {
code: `<!-- Add to pom.xml -->
<dependency>
<groupId>io.pyroscope</groupId>
<artifactId>agent</artifactId>
<version>2.1.2</version>
</dependency>
# Or download the Java agent JAR:
curl -L -o pyroscope.jar \\
https://github.com/grafana/pyroscope-java/releases/latest/download/pyroscope.jar`,
language: "bash",
};
case "dotnet":
return {
code: `dotnet add package Pyroscope
# Download the native profiler library:
curl -s -L https://github.com/grafana/pyroscope-dotnet/releases/download/v0.13.0-pyroscope/pyroscope.0.13.0-glibc-x86_64.tar.gz | tar xvz -C .`,
language: "bash",
};
case "ruby":
return {
code: `bundle add pyroscope`,
language: "bash",
};
case "rust":
return {
code: `cargo add pyroscope pyroscope_pprofrs`,
language: "bash",
};
default:
return {
code: `# Profiling SDK not available for this language.\n# Use Grafana Alloy (eBPF) for zero-code profiling instead.`,
language: "bash",
};
}
}
function getProfileConfigSnippet(lang: Language): {
code: string;
language: string;
} {
switch (lang) {
case "node":
return {
code: `const Pyroscope = require('@pyroscope/nodejs');
Pyroscope.init({
serverAddress: '<YOUR_ONEUPTIME_PYROSCOPE_URL>',
appName: 'my-service',
tags: {
region: process.env.REGION || 'default',
},
authToken: '<YOUR_ONEUPTIME_TOKEN>',
});
Pyroscope.start();`,
language: "javascript",
};
case "python":
return {
code: `import pyroscope
pyroscope.configure(
application_name="my-service",
server_address="<YOUR_ONEUPTIME_PYROSCOPE_URL>",
sample_rate=100,
tags={
"region": "us-east-1",
},
auth_token="<YOUR_ONEUPTIME_TOKEN>",
)`,
language: "python",
};
case "go":
return {
code: `package main
import (
"os"
"runtime"
"github.com/grafana/pyroscope-go"
)
func main() {
// Enable mutex and block profiling
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)
pyroscope.Start(pyroscope.Config{
ApplicationName: "my-service",
ServerAddress: "<YOUR_ONEUPTIME_PYROSCOPE_URL>",
AuthToken: os.Getenv("ONEUPTIME_TOKEN"),
Tags: map[string]string{"hostname": os.Getenv("HOSTNAME")},
ProfileTypes: []pyroscope.ProfileType{
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
pyroscope.ProfileAllocSpace,
pyroscope.ProfileInuseObjects,
pyroscope.ProfileInuseSpace,
pyroscope.ProfileGoroutines,
pyroscope.ProfileMutexCount,
pyroscope.ProfileMutexDuration,
pyroscope.ProfileBlockCount,
pyroscope.ProfileBlockDuration,
},
})
// Your application code here
}`,
language: "go",
};
case "java":
return {
code: `// Option 1: Start from code
import io.pyroscope.javaagent.PyroscopeAgent;
import io.pyroscope.javaagent.config.Config;
import io.pyroscope.javaagent.EventType;
import io.pyroscope.http.Format;
PyroscopeAgent.start(
new Config.Builder()
.setApplicationName("my-service")
.setProfilingEvent(EventType.ITIMER)
.setFormat(Format.JFR)
.setServerAddress("<YOUR_ONEUPTIME_PYROSCOPE_URL>")
.setAuthToken("<YOUR_ONEUPTIME_TOKEN>")
.build()
);
// Option 2: Attach as Java agent (no code changes)
// java -javaagent:pyroscope.jar \\
// -Dpyroscope.application.name=my-service \\
// -Dpyroscope.server.address=<YOUR_ONEUPTIME_PYROSCOPE_URL> \\
// -Dpyroscope.auth.token=<YOUR_ONEUPTIME_TOKEN> \\
// -jar my-app.jar`,
language: "java",
};
case "dotnet":
return {
code: `# Set environment variables before running your .NET application:
export PYROSCOPE_APPLICATION_NAME=my-service
export PYROSCOPE_SERVER_ADDRESS=<YOUR_ONEUPTIME_PYROSCOPE_URL>
export PYROSCOPE_AUTH_TOKEN=<YOUR_ONEUPTIME_TOKEN>
export PYROSCOPE_PROFILING_ENABLED=1
export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER={BD1A650D-AC5D-4896-B64F-D6FA25D6B26A}
export CORECLR_PROFILER_PATH=./Pyroscope.Profiler.Native.so
export LD_PRELOAD=./Pyroscope.Linux.ApiWrapper.x64.so
# Then run your application:
dotnet run`,
language: "bash",
};
case "ruby":
return {
code: `# config/initializers/pyroscope.rb
require 'pyroscope'
Pyroscope.configure do |config|
config.application_name = "my-service"
config.server_address = "<YOUR_ONEUPTIME_PYROSCOPE_URL>"
config.auth_token = "<YOUR_ONEUPTIME_TOKEN>"
config.tags = {
"hostname" => ENV["HOSTNAME"],
"region" => ENV.fetch("REGION", "default"),
}
end`,
language: "ruby",
};
case "rust":
return {
code: `use pyroscope::PyroscopeAgent;
use pyroscope_pprofrs::{pprof_backend, PprofConfig};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let pprof_config = PprofConfig::new().sample_rate(100);
let backend_impl = pprof_backend(pprof_config);
let agent = PyroscopeAgent::builder(
"<YOUR_ONEUPTIME_PYROSCOPE_URL>", "my-service"
)
.backend(backend_impl)
.auth_token("<YOUR_ONEUPTIME_TOKEN>".to_string())
.tags([("hostname", "localhost")].to_vec())
.build()?;
let agent_running = agent.start()?;
// Your application code here
let agent_ready = agent_running.stop()?;
agent_ready.shutdown();
Ok(())
}`,
language: "rust",
};
default:
return {
code: `# Profiling SDK not available for this language.\n# Use Grafana Alloy (eBPF) for zero-code profiling instead.`,
language: "bash",
};
}
}
function getAlloyEbpfSnippet(): string {
return `# alloy-config.alloy
# Grafana Alloy eBPF-based profiling — no code changes required.
# Supports: Go, Rust, C/C++, Java, Python, Ruby, PHP, Node.js, .NET
discovery.process "all" {
refresh_interval = "60s"
}
discovery.relabel "alloy_profiles" {
targets = discovery.process.all.targets
rule {
action = "replace"
source_labels = ["__meta_process_exe"]
target_label = "service_name"
}
}
pyroscope.ebpf "default" {
targets = discovery.relabel.alloy_profiles.output
forward_to = [pyroscope.write.oneuptime.receiver]
collect_interval = "15s"
sample_rate = 97
}
pyroscope.write "oneuptime" {
endpoint {
url = "<YOUR_ONEUPTIME_PYROSCOPE_URL>"
headers = {
"x-oneuptime-token" = "<YOUR_ONEUPTIME_TOKEN>",
}
}
}`;
}
function getAlloyDockerSnippet(): string {
return `# docker-compose.yml
services:
alloy:
image: grafana/alloy:latest
privileged: true
pid: host
volumes:
- ./alloy-config.alloy:/etc/alloy/config.alloy
- /proc:/proc:ro
- /sys:/sys:ro
command:
- run
- /etc/alloy/config.alloy`;
}
// --- FluentBit snippets ---
function getFluentBitSnippet(): string {
return `# fluent-bit.conf
[SERVICE]
Flush 5
Log_Level info
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/*.log
Tag app.logs
[OUTPUT]
Name opentelemetry
Match *
Host <YOUR_ONEUPTIME_OTLP_HOST>
Port 443
Tls On
Header x-oneuptime-token <YOUR_ONEUPTIME_TOKEN>
Logs_uri /v1/logs`;
}
function getFluentBitDockerSnippet(): string {
return `# docker-compose.yml
services:
fluent-bit:
image: fluent/fluent-bit:latest
volumes:
- ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
- /var/log:/var/log:ro
environment:
- FLB_ES_HOST=<YOUR_ONEUPTIME_OTLP_HOST>`;
}
// --- Fluentd snippets ---
function getFluentdSnippet(): string {
return `# fluentd.conf
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<source>
@type tail
path /var/log/*.log
pos_file /var/log/fluentd.pos
tag app.logs
<parse>
@type json
</parse>
</source>
<match **>
@type http
endpoint https://<YOUR_ONEUPTIME_OTLP_HOST>/v1/logs
headers {"x-oneuptime-token":"<YOUR_ONEUPTIME_TOKEN>"}
json_array true
<format>
@type json
</format>
<buffer>
@type memory
flush_interval 5s
</buffer>
</match>`;
}
function getFluentdDockerSnippet(): string {
return `# docker-compose.yml
services:
fluentd:
image: fluent/fluentd:latest
volumes:
- ./fluentd.conf:/fluentd/etc/fluentd.conf
- /var/log:/var/log:ro
ports:
- "24224:24224"`;
}
// --- Main Component ---
const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [selectedLanguage, setSelectedLanguage] = useState<Language>("node");
const [selectedMethod, setSelectedMethod] = useState<IntegrationMethod>(
props.telemetryType === "profiles" ? "alloy" : "opentelemetry",
);
// Token management state
const [ingestionKeys, setIngestionKeys] = useState<
Array<TelemetryIngestionKey>
>([]);
const [selectedKeyId, setSelectedKeyId] = useState<string>("");
const [isLoadingKeys, setIsLoadingKeys] = useState<boolean>(true);
const [showCreateModal, setShowCreateModal] = useState<boolean>(false);
const [keyError, setKeyError] = useState<string>("");
const telemetryType: TelemetryType = props.telemetryType || "logs";
const showLogCollectors: boolean = telemetryType === "logs";
const isProfiles: boolean = telemetryType === "profiles";
// Compute OTLP URL and host
const httpProtocol: string =
HTTP_PROTOCOL === Protocol.HTTPS ? "https" : "http";
const otlpHost: string = HOST ? HOST : "<YOUR_ONEUPTIME_OTLP_HOST>";
const otlpUrl: string = HOST
? `${httpProtocol}://${HOST}/otlp`
: "<YOUR_ONEUPTIME_URL>";
const pyroscopeUrl: string = HOST
? `${httpProtocol}://${HOST}/pyroscope`
: "<YOUR_ONEUPTIME_PYROSCOPE_URL>";
// Fetch ingestion keys on mount
useEffect(() => {
loadIngestionKeys().catch(() => {});
}, []);
const loadIngestionKeys: () => Promise<void> = async (): Promise<void> => {
try {
setIsLoadingKeys(true);
setKeyError("");
const result: ListResult<TelemetryIngestionKey> =
await ModelAPI.getList<TelemetryIngestionKey>({
modelType: TelemetryIngestionKey,
query: {
projectId: ProjectUtil.getCurrentProjectId()!,
},
limit: 50,
skip: 0,
select: {
_id: true,
name: true,
secretKey: true,
description: true,
},
sort: {},
});
setIngestionKeys(result.data);
// Auto-select the first key if available and none selected
if (result.data.length > 0 && !selectedKeyId) {
setSelectedKeyId(result.data[0]!.id?.toString() || "");
}
} catch (err) {
setKeyError(API.getFriendlyErrorMessage(err as Error));
} finally {
setIsLoadingKeys(false);
}
};
// Get the selected key object
const selectedKey: TelemetryIngestionKey | undefined = useMemo(() => {
return ingestionKeys.find((k: TelemetryIngestionKey) => {
return k.id?.toString() === selectedKeyId;
});
}, [ingestionKeys, selectedKeyId]);
// Get token string for code snippets
const tokenValue: string =
selectedKey?.secretKey?.toString() || "<YOUR_ONEUPTIME_TOKEN>";
const otlpUrlValue: string = otlpUrl;
const otlpHostValue: string = otlpHost;
const integrationMethods: Array<IntegrationOption> = useMemo(() => {
if (isProfiles) {
return [
{
key: "alloy" as IntegrationMethod,
label: "Grafana Alloy (eBPF)",
description:
"Recommended. Zero-code profiling for all languages on Linux using eBPF.",
},
{
key: "opentelemetry" as IntegrationMethod,
label: "Language SDK",
description:
"In-process profiling using Pyroscope SDKs for fine-grained control.",
},
];
}
const methods: Array<IntegrationOption> = [
{
key: "opentelemetry",
label: "OpenTelemetry",
description: "Recommended. Auto-instrumentation for most frameworks.",
},
];
if (showLogCollectors) {
methods.push({
key: "fluentbit",
label: "FluentBit",
description: "Lightweight log processor with minimal resource usage.",
});
methods.push({
key: "fluentd",
label: "Fluentd",
description: "Mature data collector with rich plugin ecosystem.",
});
}
return methods;
}, [showLogCollectors, isProfiles]);
const titleForType: Record<TelemetryType, string> = {
logs: "Log Ingestion Setup",
metrics: "Metrics Ingestion Setup",
traces: "Trace Ingestion Setup",
exceptions: "Exception Tracking Setup",
profiles: "Profiles Ingestion Setup",
};
const descriptionForType: Record<TelemetryType, string> = {
logs: "Send logs from your application to OneUptime using OpenTelemetry, FluentBit, or Fluentd.",
metrics:
"Send metrics from your application to OneUptime using OpenTelemetry SDKs.",
traces:
"Send distributed traces from your application to OneUptime using OpenTelemetry SDKs.",
exceptions:
"Capture and track exceptions from your application using OpenTelemetry SDKs.",
profiles:
"Send continuous profiling data from your application to OneUptime using OpenTelemetry SDKs.",
};
const installSnippet: { code: string; language: string } = useMemo(() => {
if (isProfiles) {
return getProfileInstallSnippet(selectedLanguage);
}
return getOtelInstallSnippet(selectedLanguage);
}, [selectedLanguage, isProfiles]);
const configSnippet: { code: string; language: string } = useMemo(() => {
if (isProfiles) {
return getProfileConfigSnippet(selectedLanguage);
}
return getOtelConfigSnippet(selectedLanguage);
}, [selectedLanguage, isProfiles]);
const handleLanguageSelect: (lang: Language) => void = useCallback(
(lang: Language) => {
setSelectedLanguage(lang);
},
[],
);
// Step component with vertical line connector
const renderStep: (
stepNumber: number,
title: string,
description: string,
content: ReactElement,
isLast?: boolean,
) => ReactElement = (
stepNumber: number,
title: string,
description: string,
content: ReactElement,
isLast?: boolean,
): ReactElement => {
return (
<div className="relative flex gap-5">
{/* Step indicator with connecting line */}
<div className="flex flex-col items-center flex-shrink-0">
<div className="w-9 h-9 rounded-full bg-indigo-50 border-2 border-indigo-500 text-indigo-600 flex items-center justify-center text-sm font-bold z-10">
{stepNumber}
</div>
{!isLast && <div className="w-0.5 flex-1 bg-gray-200 mt-2 mb-0" />}
</div>
{/* Step content */}
<div className={`flex-1 min-w-0 ${isLast ? "pb-0" : "pb-8"}`}>
<h4 className="text-sm font-semibold text-gray-900 leading-9">
{title}
</h4>
<p className="text-sm text-gray-500 mt-0.5 mb-3 leading-relaxed">
{description}
</p>
{content}
</div>
</div>
);
};
// Token step content (rendered inside Step 1)
const renderTokenStepContent: () => ReactElement = (): ReactElement => {
if (isLoadingKeys) {
return (
<div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
<p className="text-sm text-gray-500">Loading ingestion keys...</p>
</div>
);
}
if (keyError) {
return (
<div className="rounded-lg border border-red-200 bg-red-50 p-4">
<p className="text-sm text-red-600">
Failed to load ingestion keys: {keyError}
</p>
<button
type="button"
onClick={() => {
loadIngestionKeys().catch(() => {});
}}
className="mt-2 text-sm text-red-700 underline hover:text-red-800"
>
Retry
</button>
</div>
);
}
if (ingestionKeys.length === 0) {
return (
<div className="rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-6 text-center">
<div className="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center mx-auto mb-3">
<Icon icon={IconProp.Key} className="w-5 h-5 text-indigo-600" />
</div>
<p className="text-sm font-medium text-gray-900 mb-1">
No ingestion keys yet
</p>
<p className="text-xs text-gray-500 mb-4">
Create an ingestion key to authenticate your telemetry data.
</p>
<button
type="button"
onClick={() => {
setShowCreateModal(true);
}}
className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition-colors shadow-sm"
>
<Icon icon={IconProp.Add} className="w-4 h-4" />
Create Ingestion Key
</button>
</div>
);
}
return (
<div>
{/* Key selector row */}
<div className="flex items-center gap-2 mb-3">
<div className="flex-1">
<Dropdown
options={ingestionKeys.map(
(key: TelemetryIngestionKey): DropdownOption => {
return {
value: key.id?.toString() || "",
label: key.name || "Unnamed Key",
};
},
)}
value={
ingestionKeys
.filter((key: TelemetryIngestionKey) => {
return key.id?.toString() === selectedKeyId;
})
.map((key: TelemetryIngestionKey): DropdownOption => {
return {
value: key.id?.toString() || "",
label: key.name || "Unnamed Key",
};
})[0]
}
onChange={(
value: DropdownValue | Array<DropdownValue> | null,
) => {
if (value) {
setSelectedKeyId(value.toString());
}
}}
placeholder="Select an ingestion key"
ariaLabel="Select ingestion key"
/>
</div>
<button
type="button"
onClick={() => {
setShowCreateModal(true);
}}
className="inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium rounded-lg border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors flex-shrink-0"
>
<Icon icon={IconProp.Add} className="w-4 h-4" />
New Key
</button>
</div>
{/* Credentials display */}
{selectedKey && (
<div className="rounded-lg border border-gray-200 bg-white overflow-hidden">
<div className="grid grid-cols-1 divide-y divide-gray-100">
<div className="px-4 py-3 flex items-start gap-3">
<div className="w-8 h-8 rounded-md bg-blue-50 flex items-center justify-center flex-shrink-0 mt-0.5">
<Icon
icon={IconProp.Globe}
className="w-4 h-4 text-blue-600"
/>
</div>
<div className="min-w-0 flex-1">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wider">
OTLP Endpoint
</div>
<div className="text-sm text-gray-900 font-mono mt-0.5 break-all select-all">
{otlpUrlValue}
</div>
</div>
</div>
<div className="px-4 py-3 flex items-start gap-3">
<div className="w-8 h-8 rounded-md bg-amber-50 flex items-center justify-center flex-shrink-0 mt-0.5">
<Icon
icon={IconProp.Key}
className="w-4 h-4 text-amber-600"
/>
</div>
<div className="min-w-0 flex-1">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wider">
Ingestion Token
</div>
<div className="text-sm text-gray-900 font-mono mt-0.5 break-all select-all">
{selectedKey.secretKey?.toString() || "—"}
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
};
// Language selector
const renderLanguageSelector: () => ReactElement = (): ReactElement => {
const availableLanguages: Array<LanguageOption> = isProfiles
? languages.filter((l: LanguageOption) => {
return profileLanguages.includes(l.key);
})
: languages;
return (
<div className="mb-6">
<label className="block text-xs font-medium text-gray-500 uppercase tracking-wider mb-2">
Select Language
</label>
<div className="flex flex-wrap gap-1.5">
{availableLanguages.map((lang: LanguageOption) => {
const isSelected: boolean = selectedLanguage === lang.key;
return (
<button
key={lang.key}
type="button"
onClick={() => {
handleLanguageSelect(lang.key);
}}
className={`px-3.5 py-1.5 text-sm font-medium rounded-lg border transition-all ${
isSelected
? "bg-indigo-600 text-white border-indigo-600 shadow-sm"
: "bg-white text-gray-600 border-gray-200 hover:border-indigo-300 hover:text-indigo-600 hover:bg-indigo-50"
}`}
>
{lang.shortLabel}
</button>
);
})}
</div>
</div>
);
};
// Integration method selector
const renderMethodSelector: () => ReactElement = (): ReactElement => {
if (integrationMethods.length <= 1) {
return <></>;
}
return (
<div className="mb-6">
<label className="block text-xs font-medium text-gray-500 uppercase tracking-wider mb-2">
Integration Method
</label>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
{integrationMethods.map((method: IntegrationOption) => {
const isSelected: boolean = selectedMethod === method.key;
return (
<button
key={method.key}
type="button"
onClick={() => {
setSelectedMethod(method.key);
}}
className={`text-left px-4 py-3 rounded-lg border-2 transition-all ${
isSelected
? "border-indigo-500 bg-indigo-50"
: "border-gray-200 bg-white hover:border-gray-300"
}`}
>
<div
className={`text-sm font-semibold ${isSelected ? "text-indigo-700" : "text-gray-900"}`}
>
{method.label}
</div>
<div className="text-xs text-gray-500 mt-0.5 leading-relaxed">
{method.description}
</div>
</button>
);
})}
</div>
</div>
);
};
// OpenTelemetry content
const renderOpenTelemetryContent: () => ReactElement = (): ReactElement => {
return (
<div>
{renderLanguageSelector()}
<div className="mt-2">
{renderStep(
1,
"Get Your Ingestion Credentials",
"Select an existing ingestion key or create a new one. These credentials authenticate your telemetry data.",
renderTokenStepContent(),
)}
{renderStep(
2,
"Install Dependencies",
isProfiles
? `Install the Pyroscope profiling SDK for ${
languages.find((l: LanguageOption) => {
return l.key === selectedLanguage;
})?.label || selectedLanguage
}.`
: `Install the OpenTelemetry SDK and exporters for ${
languages.find((l: LanguageOption) => {
return l.key === selectedLanguage;
})?.label || selectedLanguage
}.`,
<CodeBlock
code={installSnippet.code}
language={installSnippet.language}
/>,
)}
{renderStep(
3,
isProfiles ? "Configure the Profiler" : "Configure the SDK",
isProfiles
? "Initialize the Pyroscope profiling SDK and point it to your OneUptime instance. Profiles will be continuously captured and sent."
: "Initialize OpenTelemetry with the OTLP exporter pointing to your OneUptime instance.",
<CodeBlock
code={replacePlaceholders(
configSnippet.code,
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language={configSnippet.language}
/>,
Boolean(isProfiles),
)}
{!isProfiles &&
renderStep(
4,
"Set Environment Variables (Alternative)",
"You can also configure OpenTelemetry via environment variables instead of code.",
<CodeBlock
code={replacePlaceholders(
getEnvVarSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="bash"
/>,
true,
)}
</div>
</div>
);
};
// FluentBit content
const renderFluentBitContent: () => ReactElement = (): ReactElement => {
return (
<div>
<div className="mt-2">
{renderStep(
1,
"Get Your Ingestion Credentials",
"Select an existing ingestion key or create a new one. These credentials authenticate your telemetry data.",
renderTokenStepContent(),
)}
{renderStep(
2,
"Create FluentBit Configuration",
"Create a fluent-bit.conf file that reads logs and forwards them to OneUptime via the OpenTelemetry output plugin.",
<CodeBlock
code={replacePlaceholders(
getFluentBitSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="yaml"
/>,
)}
{renderStep(
3,
"Run with Docker (Optional)",
"Run FluentBit as a Docker container alongside your application.",
<CodeBlock
code={replacePlaceholders(
getFluentBitDockerSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="yaml"
/>,
)}
{renderStep(
4,
"Run FluentBit",
"Start FluentBit with your configuration file.",
<CodeBlock code="fluent-bit -c fluent-bit.conf" language="bash" />,
true,
)}
</div>
</div>
);
};
// Fluentd content
const renderFluentdContent: () => ReactElement = (): ReactElement => {
return (
<div>
<div className="mt-2">
{renderStep(
1,
"Get Your Ingestion Credentials",
"Select an existing ingestion key or create a new one. These credentials authenticate your telemetry data.",
renderTokenStepContent(),
)}
{renderStep(
2,
"Create Fluentd Configuration",
"Create a fluentd.conf file that collects logs and sends them to OneUptime over HTTP.",
<CodeBlock
code={replacePlaceholders(
getFluentdSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="yaml"
/>,
)}
{renderStep(
3,
"Run with Docker (Optional)",
"Run Fluentd as a Docker container.",
<CodeBlock
code={replacePlaceholders(
getFluentdDockerSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="yaml"
/>,
)}
{renderStep(
4,
"Run Fluentd",
"Start Fluentd with your configuration.",
<CodeBlock code="fluentd -c fluentd.conf" language="bash" />,
true,
)}
</div>
</div>
);
};
// Grafana Alloy eBPF content (for profiles)
const renderAlloyContent: () => ReactElement = (): ReactElement => {
return (
<div>
<div className="mt-2">
{renderStep(
1,
"Get Your Ingestion Credentials",
"Select an existing ingestion key or create a new one. These credentials authenticate your profiling data.",
renderTokenStepContent(),
)}
{renderStep(
2,
"Create Alloy Configuration",
"Create an Alloy configuration file that uses eBPF to collect CPU profiles from all processes on your Linux host — no code changes required. Supports Go, Rust, C/C++, Java, Python, Ruby, PHP, Node.js, and .NET.",
<CodeBlock
code={replacePlaceholders(
getAlloyEbpfSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="hcl"
/>,
)}
{renderStep(
3,
"Run with Docker",
"Run Grafana Alloy as a privileged Docker container with access to the host PID namespace.",
<CodeBlock
code={replacePlaceholders(
getAlloyDockerSnippet(),
otlpUrlValue,
otlpHostValue,
tokenValue,
pyroscopeUrl,
)}
language="yaml"
/>,
)}
{renderStep(
4,
"Run Alloy",
"Or run Alloy directly on the host.",
<CodeBlock code="alloy run alloy-config.alloy" language="bash" />,
true,
)}
</div>
</div>
);
};
const renderActiveContent: () => ReactElement = (): ReactElement => {
switch (selectedMethod) {
case "fluentbit":
return renderFluentBitContent();
case "fluentd":
return renderFluentdContent();
case "alloy":
return renderAlloyContent();
default:
return renderOpenTelemetryContent();
}
};
return (
<div className="mb-5">
<div className="bg-white border border-gray-200 rounded-xl shadow-sm overflow-visible">
{/* Header */}
<div className="px-6 py-5 border-b border-gray-100">
<div className="flex items-start gap-4">
<div className="w-10 h-10 rounded-lg bg-indigo-50 border border-indigo-100 flex items-center justify-center flex-shrink-0">
<svg
className="w-5 h-5 text-indigo-600"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"
/>
</svg>
</div>
<div className="flex-1">
<h2 className="text-lg font-semibold text-gray-900">
{titleForType[telemetryType]}
</h2>
<p className="text-sm text-gray-500 mt-0.5 leading-relaxed">
{descriptionForType[telemetryType]}
</p>
</div>
{props.onClose && (
<button
type="button"
onClick={props.onClose}
className="flex-shrink-0 rounded-lg p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 transition-colors"
title="Close documentation"
>
<svg
className="w-5 h-5"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
)}
</div>
</div>
{/* Body */}
<div className="px-6 py-6">
{renderMethodSelector()}
{renderActiveContent()}
</div>
</div>
{/* Create Ingestion Key Modal */}
{showCreateModal && (
<ModelFormModal<TelemetryIngestionKey>
modelType={TelemetryIngestionKey}
name="Create Ingestion Key"
title="Create Ingestion Key"
description="Create a new telemetry ingestion key for sending data to OneUptime."
onClose={() => {
setShowCreateModal(false);
}}
submitButtonText="Create Key"
onSuccess={(item: TelemetryIngestionKey) => {
setShowCreateModal(false);
// Refresh the list and select the new key
loadIngestionKeys()
.then(() => {
if (item.id) {
setSelectedKeyId(item.id.toString());
}
})
.catch(() => {});
}}
formProps={{
name: "Create Ingestion Key",
modelType: TelemetryIngestionKey,
id: "create-ingestion-key",
fields: [
{
field: {
name: true,
},
title: "Name",
fieldType: FormFieldSchemaType.Text,
required: true,
placeholder: "e.g. Production Key",
validation: {
minLength: 2,
},
},
{
field: {
description: true,
},
title: "Description",
fieldType: FormFieldSchemaType.LongText,
required: false,
placeholder: "Optional description for this key",
},
],
formType: FormType.Create,
}}
onBeforeCreate={(
item: TelemetryIngestionKey,
): Promise<TelemetryIngestionKey> => {
item.projectId = ProjectUtil.getCurrentProjectId()!;
return Promise.resolve(item);
}}
/>
)}
</div>
);
};
export default TelemetryDocumentation;