Skip to content

Commit afdc004

Browse files
committed
add open telemetry logger and tracer
1 parent 410d81a commit afdc004

File tree

3 files changed

+129
-2
lines changed

3 files changed

+129
-2
lines changed

rust/cubestore/cubestore/src/telemetry/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod otel_tracing;
12
pub mod tracing;
23
use crate::CubeError;
34
use async_trait::async_trait;
@@ -10,6 +11,7 @@ use futures::{Sink, StreamExt};
1011
use futures_timer::Delay;
1112
use log::{Level, Log, Metadata, Record};
1213
use nanoid::nanoid;
14+
pub use otel_tracing::OpenTelemetryLogger;
1315
use reqwest::header::HeaderMap;
1416
use serde_json::{Map, Number, Value};
1517
use std::collections::{HashMap, HashSet};
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use lazy_static::lazy_static;
2+
use log::{Level, Log, Metadata, Record};
3+
use opentelemetry::global::ObjectSafeSpan;
4+
use opentelemetry::logs::AnyValue::String;
5+
use opentelemetry::logs::{LogRecord, Logger, LoggerProvider};
6+
use opentelemetry::trace::{SpanKind, Tracer, TracerProvider};
7+
use opentelemetry::{KeyValue, StringValue};
8+
use opentelemetry_sdk::logs::{LogRecord as LogRecordSDK, Logger as LoggerSDK};
9+
use opentelemetry_sdk::trace::Tracer as TracerSDK;
10+
11+
const OTEL_SERVICE_NAME: &str = "cubestore";
12+
13+
lazy_static! {
14+
pub static ref OT_TRACER: TracerSDK = init_tracing().unwrap();
15+
pub static ref OT_LOGGER: LoggerSDK = init_logging().unwrap();
16+
}
17+
18+
pub fn init_tracing() -> Result<TracerSDK, Box<dyn std::error::Error>> {
19+
let otlp_exporter = opentelemetry_otlp::new_exporter()
20+
.http()
21+
.with_http_client(reqwest::Client::new());
22+
let tracer_provider = opentelemetry_otlp::new_pipeline()
23+
.tracing()
24+
.with_exporter(otlp_exporter)
25+
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
26+
27+
let tracer = tracer_provider.tracer_builder(OTEL_SERVICE_NAME).build();
28+
Ok(tracer)
29+
}
30+
31+
pub fn init_logging() -> Result<LoggerSDK, Box<dyn std::error::Error>> {
32+
let otlp_exporter = opentelemetry_otlp::new_exporter()
33+
.http()
34+
.with_http_client(reqwest::Client::new());
35+
let logger_provider = opentelemetry_otlp::new_pipeline()
36+
.logging()
37+
.with_exporter(otlp_exporter)
38+
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
39+
40+
let logger = logger_provider.logger_builder(OTEL_SERVICE_NAME).build();
41+
Ok(logger)
42+
}
43+
44+
pub struct OpenTelemetryLogger {
45+
logger: Box<dyn Log>,
46+
}
47+
48+
impl OpenTelemetryLogger {
49+
pub fn new(logger: Box<dyn Log>) -> Self {
50+
Self { logger }
51+
}
52+
}
53+
54+
impl Log for OpenTelemetryLogger {
55+
fn enabled<'a>(&self, metadata: &Metadata<'a>) -> bool {
56+
self.logger.enabled(metadata)
57+
}
58+
59+
fn log<'a>(&self, record: &Record<'a>) {
60+
if !self.enabled(record.metadata()) {
61+
return;
62+
}
63+
self.logger.log(&record);
64+
65+
match record.metadata().level() {
66+
Level::Error => {
67+
// Log error messages using OpenTelemetry logger
68+
let logger = &*OT_LOGGER;
69+
emit_log(record, logger);
70+
}
71+
Level::Warn => {
72+
// Log warning messages using OpenTelemetry logger
73+
let logger = &*OT_LOGGER;
74+
emit_log(record, logger);
75+
}
76+
Level::Info => {}
77+
Level::Debug => {
78+
// Create an OpenTelemetry trace for Debug level
79+
let tracer = &*OT_TRACER;
80+
create_log_trace(record, tracer);
81+
}
82+
Level::Trace => {
83+
// Create an OpenTelemetry trace for Trace level
84+
let tracer = &*OT_TRACER;
85+
create_log_trace(record, tracer);
86+
}
87+
}
88+
}
89+
90+
fn flush(&self) {
91+
self.logger.flush()
92+
}
93+
}
94+
95+
fn emit_log(record: &Record, logger: &LoggerSDK) {
96+
let mut rec = LogRecordSDK::default();
97+
98+
rec.set_target(record.target().to_string());
99+
rec.set_severity_text(record.level().as_str());
100+
rec.set_body(String(StringValue::from(record.args().to_string())));
101+
102+
logger.emit(rec);
103+
}
104+
105+
fn create_log_trace(record: &Record, tracer: &TracerSDK) {
106+
let mut span = tracer
107+
.span_builder(format!(
108+
"{} ({})",
109+
record.module_path().unwrap_or(record.target()),
110+
record.file().unwrap_or("-")
111+
))
112+
.with_kind(SpanKind::Server)
113+
.with_attributes([
114+
KeyValue::new("level", record.level().as_str()),
115+
KeyValue::new("target", record.target().to_string()),
116+
KeyValue::new("message", record.args().to_string()),
117+
])
118+
.start(tracer);
119+
span.end();
120+
}

rust/cubestore/cubestore/src/util/logger.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::telemetry::ReportingLogger;
1+
use crate::telemetry::{OpenTelemetryLogger, ReportingLogger};
22
use log::{Level, Log, Metadata, Record};
33
use simple_logger::SimpleLogger;
44
use std::env;
@@ -30,7 +30,12 @@ pub fn init_cube_logger(enable_telemetry: bool) {
3030
}
3131
let mut logger: Box<dyn Log> = Box::new(ContextLogger::new(ctx, logger));
3232
if enable_telemetry {
33-
logger = Box::new(ReportingLogger::new(logger))
33+
// No need to introduce special env vars, let's use the de facto standard ones
34+
if let Ok(_) = env::var("OTEL_EXPORTER_OTLP_ENDPOINT") {
35+
logger = Box::new(OpenTelemetryLogger::new(logger));
36+
} else {
37+
logger = Box::new(ReportingLogger::new(logger));
38+
}
3439
}
3540

3641
log::set_boxed_logger(logger).expect("Failed to initialize logger");

0 commit comments

Comments
 (0)