use opentelemetry_sdk::Resource;
use opentelemetry_sdk::logs::{BatchLogProcessor, LoggerProviderBuilder, SdkLoggerProvider};
use crate::InitError;
use crate::config::logs::{BatchLogRecordProcessorConfig, RateLimitedLogRecordProcessorConfig};
use crate::config::{
BatchProcessorConfig, LogExporter, LogRecordProcessor, OpenTelemetryConfig,
RateLimitedProcessorConfig,
};
use crate::error::ExporterKind;
#[cfg(any(
feature = "otlp",
feature = "honeycomb",
feature = "new-relic",
feature = "grafana-cloud",
feature = "datadog-otlp"
))]
use crate::exporters::instrumented::InstrumentedLogExporter;
use crate::processors::logs::RateLimitedLogProcessor;
pub(crate) fn build_logger_provider(
config: &OpenTelemetryConfig,
resource: Resource,
mut builder: LoggerProviderBuilder,
) -> Result<SdkLoggerProvider, InitError> {
builder = builder.with_resource(resource);
for processor in &config.logger_provider.processors {
builder = add_log_processor(builder, processor)?;
}
Ok(builder.build())
}
fn add_log_processor(
builder: LoggerProviderBuilder,
config: &LogRecordProcessor,
) -> Result<LoggerProviderBuilder, InitError> {
match config {
LogRecordProcessor::Batch(batch_config) => add_batch_log_processor(builder, batch_config),
LogRecordProcessor::Simple(simple_config) => {
add_simple_log_processor(builder, &simple_config.exporter)
}
LogRecordProcessor::RateLimited(config) => add_rate_limited_log_processor(builder, config),
}
}
fn add_simple_log_processor(
#[allow(unused_variables)] builder: LoggerProviderBuilder,
exporter_config: &LogExporter,
) -> Result<LoggerProviderBuilder, InitError> {
match exporter_config {
#[cfg(feature = "otlp")]
LogExporter::OtlpHttp(config) => {
let exporter = crate::exporters::otlp::build_otlp_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "otlp")]
LogExporter::OtlpGrpc(config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombHttp(config) => {
let exporter = crate::exporters::honeycomb::build_http_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombGrpc(config) => {
let exporter = crate::exporters::honeycomb::build_grpc_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicHttp(config) => {
let exporter = crate::exporters::new_relic::build_http_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicGrpc(config) => {
let exporter = crate::exporters::new_relic::build_grpc_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "grafana-cloud")]
LogExporter::GrafanaCloud(config) => {
let exporter = crate::exporters::grafana_cloud::build_http_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpHttp(config) => {
let exporter = crate::exporters::datadog_otlp::build_http_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpGrpc(config) => {
let exporter = crate::exporters::datadog_otlp::build_grpc_log_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
LogExporter::Console(config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutLogExporter::with_writer(
config.format.into(),
config.target.into(),
config.use_color(),
);
Ok(builder.with_simple_exporter(exporter))
}
}
}
fn add_batch_log_processor(
#[allow(unused_variables)] builder: LoggerProviderBuilder,
config: &BatchLogRecordProcessorConfig,
) -> Result<LoggerProviderBuilder, InitError> {
#[allow(unused_variables)]
let batch_config = config.to_logs_batch_config();
match &config.exporter {
#[cfg(feature = "otlp")]
LogExporter::OtlpHttp(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::OtlpHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "otlp")]
LogExporter::OtlpGrpc(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::OtlpGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombHttp(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::HoneycombHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombGrpc(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::HoneycombGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicHttp(exporter_config) => {
let exporter = crate::exporters::new_relic::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::NewRelicHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicGrpc(exporter_config) => {
let exporter = crate::exporters::new_relic::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::NewRelicGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "grafana-cloud")]
LogExporter::GrafanaCloud(exporter_config) => {
let exporter =
crate::exporters::grafana_cloud::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::GrafanaCloudHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpHttp(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::DatadogHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpGrpc(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::DatadogGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
LogExporter::Console(config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutLogExporter::with_writer(
config.format.into(),
config.target.into(),
config.use_color(),
);
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_log_processor(batch_processor))
}
}
}
#[allow(unused_variables)]
fn add_rate_limited_log_processor(
builder: LoggerProviderBuilder,
config: &RateLimitedLogRecordProcessorConfig,
) -> Result<LoggerProviderBuilder, InitError> {
let batch_config = config.to_logs_batch_config();
let max_rate = config.max_rate();
match &config.exporter {
#[cfg(feature = "otlp")]
LogExporter::OtlpHttp(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::OtlpHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::OtlpHttp,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "otlp")]
LogExporter::OtlpGrpc(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::OtlpGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::OtlpGrpc,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombHttp(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::HoneycombHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::HoneycombHttp,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "honeycomb")]
LogExporter::HoneycombGrpc(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::HoneycombGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::HoneycombGrpc,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicHttp(exporter_config) => {
let exporter = crate::exporters::new_relic::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::NewRelicHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::NewRelicHttp,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "new-relic")]
LogExporter::NewRelicGrpc(exporter_config) => {
let exporter = crate::exporters::new_relic::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::NewRelicGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::NewRelicGrpc,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "grafana-cloud")]
LogExporter::GrafanaCloud(exporter_config) => {
let exporter =
crate::exporters::grafana_cloud::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::GrafanaCloudHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::GrafanaCloudHttp,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpHttp(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_http_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::DatadogHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::DatadogHttp,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
#[cfg(feature = "datadog-otlp")]
LogExporter::DatadogOtlpGrpc(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_grpc_log_exporter(exporter_config)?;
let exporter = InstrumentedLogExporter::new(
exporter,
ExporterKind::DatadogGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::DatadogGrpc,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
LogExporter::Console(exporter_config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutLogExporter::with_writer(
exporter_config.format.into(),
exporter_config.target.into(),
exporter_config.use_color(),
);
let exporter = InstrumentedLogExporter::new(exporter, ExporterKind::Console, None);
let instance_id = exporter.instance_id();
let batch_processor = BatchLogProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedLogProcessor::new(
batch_processor,
max_rate,
ExporterKind::Console,
instance_id,
);
Ok(builder.with_log_processor(rate_limited))
}
}
}
#[cfg(test)]
mod tests {
use apollo_configuration::parse_yaml;
use opentelemetry_sdk::logs::SdkLoggerProvider;
use super::*;
#[cfg(feature = "otlp")]
#[test]
fn build_logger_provider_with_otlp_http() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- batch:
exporter:
otlp_http:
endpoint: http://localhost:4318
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_logger_provider_with_resource() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
resource:
attributes:
- name: service.name
value: test-service
logger_provider:
processors:
- batch:
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_logger_provider_with_simple_processor() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- simple:
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_logger_provider_with_batch_processor_custom_settings() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- batch:
schedule_delay: 1000
max_export_batch_size: 2500
max_queue_size: 1024
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_logger_provider_with_rate_limited_processor() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_logger_provider_with_rate_limited_processor_custom_batch_settings() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 500
schedule_delay: 1000
max_export_batch_size: 2500
max_queue_size: 1024
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "otlp")]
#[tokio::test]
async fn build_logger_provider_with_rate_limited_otlp_grpc() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
otlp_grpc:
endpoint: http://localhost:4317
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "honeycomb")]
#[tokio::test]
async fn build_logger_provider_with_rate_limited_honeycomb() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
honeycomb_http:
api_key: test-key
endpoint: https://api.honeycomb.io
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "new-relic")]
#[tokio::test]
async fn build_logger_provider_with_rate_limited_new_relic() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
new_relic_http:
license_key: test-key
endpoint: https://otlp.nr-data.net:4318
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "datadog-otlp")]
#[tokio::test]
async fn build_logger_provider_with_rate_limited_datadog() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
logger_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
datadog_otlp_http:
api_key: test-key
endpoint: https://http-intake.logs.datadoghq.com/api/v2/logs
"},
&Default::default(),
)
.unwrap();
let builder = SdkLoggerProvider::builder();
let result = build_logger_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
}