Skip to content

Commit 83f1abd

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

File tree

3 files changed

+127
-2
lines changed

3 files changed

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

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)