feat: Factor out tracing/logging CLI options
This PR factors out the tracing/logging CLI optinos into the `trogging` utility crate, so that multiple binaries from the IOx suite (such as conductor) can use the same (and quite complex) logging/tracing configuration options (flags and env vars). Closes influxdata/conductor#343pull/24376/head
parent
38d17a3093
commit
760bcde3f0
|
@ -4579,6 +4579,7 @@ dependencies = [
|
||||||
"opentelemetry-jaeger",
|
"opentelemetry-jaeger",
|
||||||
"opentelemetry-otlp",
|
"opentelemetry-otlp",
|
||||||
"regex",
|
"regex",
|
||||||
|
"structopt",
|
||||||
"synchronized-writer",
|
"synchronized-writer",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing-opentelemetry",
|
"tracing-opentelemetry",
|
||||||
|
|
|
@ -63,7 +63,7 @@ query = { path = "query" }
|
||||||
read_buffer = { path = "read_buffer" }
|
read_buffer = { path = "read_buffer" }
|
||||||
server = { path = "server" }
|
server = { path = "server" }
|
||||||
tracker = { path = "tracker" }
|
tracker = { path = "tracker" }
|
||||||
trogging = { path = "trogging" }
|
trogging = { path = "trogging", features = ["structopt"] }
|
||||||
|
|
||||||
# Crates.io dependencies, in alphabetical order
|
# Crates.io dependencies, in alphabetical order
|
||||||
arrow = { version = "4.0", features = ["prettyprint"] }
|
arrow = { version = "4.0", features = ["prettyprint"] }
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
//! Implementation of command line option for running server
|
//! Implementation of command line option for running server
|
||||||
|
|
||||||
use crate::commands::tracing;
|
|
||||||
use crate::influxdb_ioxd::{self, serving_readiness::ServingReadinessState};
|
use crate::influxdb_ioxd::{self, serving_readiness::ServingReadinessState};
|
||||||
use clap::arg_enum;
|
use clap::arg_enum;
|
||||||
use core::num::NonZeroU16;
|
|
||||||
use data_types::server_id::ServerId;
|
use data_types::server_id::ServerId;
|
||||||
use std::{net::SocketAddr, net::ToSocketAddrs, path::PathBuf};
|
use std::{net::SocketAddr, net::ToSocketAddrs, path::PathBuf};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use trogging::cli::TracingConfig;
|
||||||
|
|
||||||
/// The default bind address for the HTTP API.
|
/// The default bind address for the HTTP API.
|
||||||
pub const DEFAULT_API_BIND_ADDR: &str = "127.0.0.1:8080";
|
pub const DEFAULT_API_BIND_ADDR: &str = "127.0.0.1:8080";
|
||||||
|
@ -43,233 +42,9 @@ Configuration is loaded from the following sources (highest precedence first):
|
||||||
- pre-configured default values"
|
- pre-configured default values"
|
||||||
)]
|
)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Logs: filter directive
|
// logging and tracing options
|
||||||
///
|
#[structopt(flatten)]
|
||||||
/// Configures log severity level filter, by target.
|
pub(crate) tracing_config: TracingConfig,
|
||||||
///
|
|
||||||
/// Simplest options: error, warn, info, debug, trace
|
|
||||||
///
|
|
||||||
/// Levels for different modules can be specified. For example
|
|
||||||
/// `debug,hyper::proto::h1=info` specifies debug logging for all modules
|
|
||||||
/// except for the `hyper::proto::h1' module which will only display info
|
|
||||||
/// level logging.
|
|
||||||
///
|
|
||||||
/// Extended syntax provided by `tracing-subscriber` includes span/field
|
|
||||||
/// filters. See <https://docs.rs/tracing-subscriber/0.2.17/tracing_subscriber/filter/struct.EnvFilter.html> for more details.
|
|
||||||
#[structopt(long = "--log-filter", env = "LOG_FILTER", default_value = "warn")]
|
|
||||||
pub log_filter: String,
|
|
||||||
|
|
||||||
/// Logs: filter short-hand
|
|
||||||
///
|
|
||||||
/// Convenient way to set log severity level filter.
|
|
||||||
/// Overrides `--log-filter`.
|
|
||||||
///
|
|
||||||
/// -v 'info'
|
|
||||||
///
|
|
||||||
/// -vv 'debug,hyper::proto::h1=info,h2=info'
|
|
||||||
///
|
|
||||||
/// -vvv 'trace,hyper::proto::h1=info,h2=info'
|
|
||||||
#[structopt(
|
|
||||||
short = "-v",
|
|
||||||
long = "--verbose",
|
|
||||||
multiple = true,
|
|
||||||
takes_value = false,
|
|
||||||
parse(from_occurrences)
|
|
||||||
)]
|
|
||||||
pub log_verbose_count: u8,
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
/// Logs: message format
|
|
||||||
///
|
|
||||||
/// Can be one of:
|
|
||||||
///
|
|
||||||
/// full: human-readable, single line
|
|
||||||
///
|
|
||||||
/// Oct 24 12:55:47.815 ERROR shaving_yaks{yaks=3}: fmt::yak_shave: failed to shave yak yak=3 error=missing yak
|
|
||||||
/// Oct 24 12:55:47.815 TRACE shaving_yaks{yaks=3}: fmt::yak_shave: yaks_shaved=2
|
|
||||||
/// Oct 24 12:55:47.815 INFO fmt: yak shaving completed all_yaks_shaved=false
|
|
||||||
///
|
|
||||||
/// pretty: human-readable, multi line
|
|
||||||
///
|
|
||||||
/// Oct 24 12:57:29.387 fmt_pretty::yak_shave: failed to shave yak, yak: 3, error: missing yak
|
|
||||||
/// at examples/examples/fmt/yak_shave.rs:48 on main
|
|
||||||
/// in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
|
|
||||||
///
|
|
||||||
/// Oct 24 12:57:29.387 fmt_pretty::yak_shave: yaks_shaved: 2
|
|
||||||
/// at examples/examples/fmt/yak_shave.rs:52 on main
|
|
||||||
/// in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
|
|
||||||
///
|
|
||||||
/// Oct 24 12:57:29.387 fmt_pretty: yak shaving completed, all_yaks_shaved: false
|
|
||||||
/// at examples/examples/fmt-pretty.rs:19 on main
|
|
||||||
///
|
|
||||||
/// json: machine-parseable
|
|
||||||
///
|
|
||||||
/// {"timestamp":"Oct 24 13:00:00.875","level":"ERROR","fields":{"message":"failed to shave yak","yak":3,"error":"missing yak"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
|
|
||||||
/// {"timestamp":"Oct 24 13:00:00.875","level":"TRACE","fields":{"yaks_shaved":2},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
|
|
||||||
/// {"timestamp":"Oct 24 13:00:00.875","level":"INFO","fields":{"message":"yak shaving completed","all_yaks_shaved":false},"target":"fmt_json"}
|
|
||||||
///
|
|
||||||
/// logfmt: human-readable and machine-parseable
|
|
||||||
///
|
|
||||||
/// level=info msg="This is an info message" target="logging" location="logfmt/tests/logging.rs:36" time=1612181556329599000
|
|
||||||
/// level=debug msg="This is a debug message" target="logging" location="logfmt/tests/logging.rs:37" time=1612181556329618000
|
|
||||||
/// level=trace msg="This is a trace message" target="logging" location="logfmt/tests/logging.rs:38" time=1612181556329634000
|
|
||||||
#[structopt(long = "--log-format", env = "LOG_FORMAT", default_value = "full", verbatim_doc_comment)]
|
|
||||||
pub log_format: tracing::LogFormat,
|
|
||||||
|
|
||||||
/// Logs: destination
|
|
||||||
///
|
|
||||||
/// Can be one of: stdout, stderr
|
|
||||||
//
|
|
||||||
// TODO(jacobmarble): consider adding file path, file rotation, syslog, ?
|
|
||||||
#[structopt(
|
|
||||||
long = "--log-destination",
|
|
||||||
env = "LOG_DESTINATION",
|
|
||||||
default_value = "stdout",
|
|
||||||
verbatim_doc_comment
|
|
||||||
)]
|
|
||||||
pub log_destination: tracing::LogDestination,
|
|
||||||
|
|
||||||
/// Tracing: exporter type
|
|
||||||
///
|
|
||||||
/// Can be one of: none, jaeger, otlp
|
|
||||||
///
|
|
||||||
/// When enabled, additional flags are considered (see flags related to OTLP
|
|
||||||
/// and Jaeger), and log output is disabled.
|
|
||||||
//
|
|
||||||
// TODO(jacobmarble): allow logs and traces simultaneously
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter",
|
|
||||||
env = "TRACES_EXPORTER",
|
|
||||||
default_value = "none"
|
|
||||||
)]
|
|
||||||
pub traces_exporter: tracing::TracesExporter,
|
|
||||||
|
|
||||||
/// Tracing: filter directive
|
|
||||||
///
|
|
||||||
/// Configures traces severity level filter, by target.
|
|
||||||
///
|
|
||||||
/// Simplest options: error, warn, info, debug, trace
|
|
||||||
///
|
|
||||||
/// Levels for different modules can be specified. For example
|
|
||||||
/// `debug,hyper::proto::h1=info` specifies debug tracing for all modules
|
|
||||||
/// except for the `hyper::proto::h1` module which will only display info
|
|
||||||
/// level tracing.
|
|
||||||
///
|
|
||||||
/// Extended syntax provided by `tracing-subscriber` includes span/field
|
|
||||||
/// filters. See <https://docs.rs/tracing-subscriber/0.2.17/tracing_subscriber/filter/struct.EnvFilter.html> for more details.
|
|
||||||
///
|
|
||||||
/// No filter by default.
|
|
||||||
#[structopt(long = "--traces-filter", env = "TRACES_FILTER")]
|
|
||||||
pub traces_filter: Option<String>,
|
|
||||||
|
|
||||||
/// Tracing: sampler type
|
|
||||||
///
|
|
||||||
/// Can be one of:
|
|
||||||
/// always_on, always_off, traceidratio,
|
|
||||||
/// parentbased_always_on, parentbased_always_off, parentbased_traceidratio
|
|
||||||
///
|
|
||||||
/// These alternatives are described in detail at <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/sdk-environment-variables.md#general-sdk-configuration>.
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-sampler",
|
|
||||||
env = "TRACES_SAMPLER",
|
|
||||||
default_value = "parentbased_traceidratio"
|
|
||||||
)]
|
|
||||||
pub traces_sampler: tracing::TracesSampler,
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
/// Tracing: sampler argument
|
|
||||||
///
|
|
||||||
/// Valid range: [0.0, 1.0].
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-sampler` is set to
|
|
||||||
/// parentbased_traceidratio (default) or traceidratio.
|
|
||||||
///
|
|
||||||
/// With sample parentbased_traceidratio, the following rules apply:
|
|
||||||
/// - if parent is sampled, then all of its children are sampled
|
|
||||||
/// - else sample this portion of traces (0.5 = 50%)
|
|
||||||
///
|
|
||||||
/// More details about this sampling argument at <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/sdk-environment-variables.md#general-sdk-configuration>.
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-sampler-arg",
|
|
||||||
env = "TRACES_SAMPLER_ARG",
|
|
||||||
default_value = "1.0",
|
|
||||||
verbatim_doc_comment
|
|
||||||
)]
|
|
||||||
pub traces_sampler_arg: f64,
|
|
||||||
|
|
||||||
/// Tracing: OTLP (eg OpenTelemetry collector) network hostname
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "otlp".
|
|
||||||
///
|
|
||||||
/// Protocol is gRPC. HTTP is not supported.
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-otlp-host",
|
|
||||||
env = "TRACES_EXPORTER_OTLP_HOST",
|
|
||||||
default_value = "localhost"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_otlp_host: String,
|
|
||||||
|
|
||||||
/// Tracing: OTLP (eg OpenTelemetry collector) network port
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "otlp".
|
|
||||||
///
|
|
||||||
/// Protocol is gRPC. HTTP is not supported.
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-otlp-port",
|
|
||||||
env = "TRACES_EXPORTER_OTLP_PORT",
|
|
||||||
default_value = "4317"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_otlp_port: NonZeroU16,
|
|
||||||
|
|
||||||
/// Tracing: Jaeger agent network hostname
|
|
||||||
///
|
|
||||||
/// Protocol is Thrift/Compact over UDP.
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "jaeger".
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-jaeger-agent-host",
|
|
||||||
env = "TRACES_EXPORTER_JAEGER_AGENT_HOST",
|
|
||||||
default_value = "0.0.0.0"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_jaeger_agent_host: String,
|
|
||||||
|
|
||||||
/// Tracing: Jaeger agent network port
|
|
||||||
///
|
|
||||||
/// Protocol is Thrift/Compact over UDP.
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "jaeger".
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-jaeger-agent-port",
|
|
||||||
env = "TRACES_EXPORTER_JAEGER_AGENT_PORT",
|
|
||||||
default_value = "6831"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_jaeger_agent_port: NonZeroU16,
|
|
||||||
|
|
||||||
/// Tracing: Jaeger service name.
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "jaeger".
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-jaeger-service-name",
|
|
||||||
env = "TRACES_EXPORTER_JAEGER_SERVICE_NAME",
|
|
||||||
default_value = "iox"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_jaeger_service_name: String,
|
|
||||||
|
|
||||||
/// Tracing: Jaeger max UDP packet size
|
|
||||||
///
|
|
||||||
/// Default to 1300, which is a safe MTU.
|
|
||||||
///
|
|
||||||
/// You can increase it to 65000 if the target is a jaeger collector
|
|
||||||
/// on localhost. If so, the batching exporter will be enabled for
|
|
||||||
/// extra efficiency. Otherwise an UDP packet will be sent for each exported span.
|
|
||||||
///
|
|
||||||
/// Only used if `--traces-exporter` is "jaeger".
|
|
||||||
#[structopt(
|
|
||||||
long = "--traces-exporter-jaeger-max-packet-size",
|
|
||||||
env = "TRACES_EXPORTER_JAEGER_MAX_PACKET_SIZE",
|
|
||||||
default_value = "1300"
|
|
||||||
)]
|
|
||||||
pub traces_exporter_jaeger_max_packet_size: usize,
|
|
||||||
|
|
||||||
/// The identifier for the server.
|
/// The identifier for the server.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! Log and trace initialization and setup
|
//! Log and trace initialization and setup
|
||||||
|
|
||||||
|
use std::cmp::max;
|
||||||
pub use trogging::config::*;
|
pub use trogging::config::*;
|
||||||
pub use trogging::TracingGuard;
|
pub use trogging::TracingGuard;
|
||||||
use trogging::{JaegerConfig, OtlpConfig};
|
|
||||||
|
|
||||||
/// Start simple logger. Panics on error.
|
/// Start simple logger. Panics on error.
|
||||||
pub fn init_simple_logs(log_verbose_count: u8) -> Result<TracingGuard, trogging::Error> {
|
pub fn init_simple_logs(log_verbose_count: u8) -> Result<TracingGuard, trogging::Error> {
|
||||||
|
@ -16,33 +16,11 @@ pub fn init_logs_and_tracing(
|
||||||
log_verbose_count: u8,
|
log_verbose_count: u8,
|
||||||
config: &crate::commands::run::Config,
|
config: &crate::commands::run::Config,
|
||||||
) -> Result<TracingGuard, trogging::Error> {
|
) -> Result<TracingGuard, trogging::Error> {
|
||||||
|
let mut config = config.tracing_config.clone();
|
||||||
|
|
||||||
// Handle the case if -v/-vv is specified both before and after the server
|
// Handle the case if -v/-vv is specified both before and after the server
|
||||||
// command
|
// command
|
||||||
let log_verbose_count = if log_verbose_count > config.log_verbose_count {
|
config.log_verbose_count = max(config.log_verbose_count, log_verbose_count);
|
||||||
log_verbose_count
|
|
||||||
} else {
|
|
||||||
config.log_verbose_count
|
|
||||||
};
|
|
||||||
|
|
||||||
trogging::Builder::new()
|
config.to_builder().install_global()
|
||||||
.with_log_filter(&config.log_filter)
|
|
||||||
// with_verbose_count goes after with_log_filter because our CLI flag state
|
|
||||||
// that --v overrides --log-filter.
|
|
||||||
.with_log_verbose_count(log_verbose_count)
|
|
||||||
.with_log_destination(config.log_destination)
|
|
||||||
.with_log_format(config.log_format)
|
|
||||||
.with_traces_filter(&config.traces_filter)
|
|
||||||
.with_traces_exporter(config.traces_exporter)
|
|
||||||
.with_traces_sampler(config.traces_sampler, config.traces_sampler_arg)
|
|
||||||
.with_jaeger_config(JaegerConfig {
|
|
||||||
agent_host: config.traces_exporter_jaeger_agent_host.clone(),
|
|
||||||
agent_port: config.traces_exporter_jaeger_agent_port,
|
|
||||||
service_name: config.traces_exporter_jaeger_service_name.clone(),
|
|
||||||
max_packet_size: config.traces_exporter_jaeger_max_packet_size,
|
|
||||||
})
|
|
||||||
.with_oltp_config(OtlpConfig {
|
|
||||||
host: config.traces_exporter_otlp_host.clone(),
|
|
||||||
port: config.traces_exporter_otlp_port,
|
|
||||||
})
|
|
||||||
.install_global()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ opentelemetry-jaeger = { version = "0.12", features = ["tokio"] }
|
||||||
opentelemetry-otlp = "0.6"
|
opentelemetry-otlp = "0.6"
|
||||||
thiserror = "1.0.23"
|
thiserror = "1.0.23"
|
||||||
tracing-opentelemetry = { version = "0.12", default-features = false }
|
tracing-opentelemetry = { version = "0.12", default-features = false }
|
||||||
|
structopt = { version = "0.3.21", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
synchronized-writer = "1"
|
synchronized-writer = "1"
|
||||||
|
|
|
@ -0,0 +1,337 @@
|
||||||
|
///! Common CLI flags for logging and tracing
|
||||||
|
use crate::{config::*, Builder, Result, TracingGuard};
|
||||||
|
use observability_deps::tracing_subscriber::fmt::writer::BoxMakeWriter;
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
/// CLI config for the logging+tracing related subset of options.
|
||||||
|
#[derive(Debug, StructOpt, Clone)]
|
||||||
|
pub struct TracingConfig {
|
||||||
|
/// Logs: filter directive
|
||||||
|
///
|
||||||
|
/// Configures log severity level filter, by target.
|
||||||
|
///
|
||||||
|
/// Simplest options: error, warn, info, debug, trace
|
||||||
|
///
|
||||||
|
/// Levels for different modules can be specified. For example
|
||||||
|
/// `debug,hyper::proto::h1=info` specifies debug logging for all modules
|
||||||
|
/// except for the `hyper::proto::h1' module which will only display info
|
||||||
|
/// level logging.
|
||||||
|
///
|
||||||
|
/// Extended syntax provided by `tracing-subscriber` includes span/field
|
||||||
|
/// filters. See <https://docs.rs/tracing-subscriber/0.2.17/tracing_subscriber/filter/struct.EnvFilter.html> for more details.
|
||||||
|
#[structopt(long = "--log-filter", env = "LOG_FILTER", default_value = "warn")]
|
||||||
|
pub log_filter: String,
|
||||||
|
|
||||||
|
/// Logs: filter short-hand
|
||||||
|
///
|
||||||
|
/// Convenient way to set log severity level filter.
|
||||||
|
/// Overrides `--log-filter`.
|
||||||
|
///
|
||||||
|
/// -v 'info'
|
||||||
|
///
|
||||||
|
/// -vv 'debug,hyper::proto::h1=info,h2=info'
|
||||||
|
///
|
||||||
|
/// -vvv 'trace,hyper::proto::h1=info,h2=info'
|
||||||
|
#[structopt(
|
||||||
|
short = "-v",
|
||||||
|
long = "--verbose",
|
||||||
|
multiple = true,
|
||||||
|
takes_value = false,
|
||||||
|
parse(from_occurrences)
|
||||||
|
)]
|
||||||
|
pub log_verbose_count: u8,
|
||||||
|
|
||||||
|
/// Logs: destination
|
||||||
|
///
|
||||||
|
/// Can be one of: stdout, stderr
|
||||||
|
//
|
||||||
|
// TODO(jacobmarble): consider adding file path, file rotation, syslog, ?
|
||||||
|
#[structopt(
|
||||||
|
long = "--log-destination",
|
||||||
|
env = "LOG_DESTINATION",
|
||||||
|
default_value = "stdout",
|
||||||
|
verbatim_doc_comment
|
||||||
|
)]
|
||||||
|
pub log_destination: LogDestination,
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
/// Logs: message format
|
||||||
|
///
|
||||||
|
/// Can be one of:
|
||||||
|
///
|
||||||
|
/// full: human-readable, single line
|
||||||
|
///
|
||||||
|
/// Oct 24 12:55:47.815 ERROR shaving_yaks{yaks=3}: fmt::yak_shave: failed to shave yak yak=3 error=missing yak
|
||||||
|
/// Oct 24 12:55:47.815 TRACE shaving_yaks{yaks=3}: fmt::yak_shave: yaks_shaved=2
|
||||||
|
/// Oct 24 12:55:47.815 INFO fmt: yak shaving completed all_yaks_shaved=false
|
||||||
|
///
|
||||||
|
/// pretty: human-readable, multi line
|
||||||
|
///
|
||||||
|
/// Oct 24 12:57:29.387 fmt_pretty::yak_shave: failed to shave yak, yak: 3, error: missing yak
|
||||||
|
/// at examples/examples/fmt/yak_shave.rs:48 on main
|
||||||
|
/// in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
|
||||||
|
///
|
||||||
|
/// Oct 24 12:57:29.387 fmt_pretty::yak_shave: yaks_shaved: 2
|
||||||
|
/// at examples/examples/fmt/yak_shave.rs:52 on main
|
||||||
|
/// in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
|
||||||
|
///
|
||||||
|
/// Oct 24 12:57:29.387 fmt_pretty: yak shaving completed, all_yaks_shaved: false
|
||||||
|
/// at examples/examples/fmt-pretty.rs:19 on main
|
||||||
|
///
|
||||||
|
/// json: machine-parseable
|
||||||
|
///
|
||||||
|
/// {"timestamp":"Oct 24 13:00:00.875","level":"ERROR","fields":{"message":"failed to shave yak","yak":3,"error":"missing yak"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
|
||||||
|
/// {"timestamp":"Oct 24 13:00:00.875","level":"TRACE","fields":{"yaks_shaved":2},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
|
||||||
|
/// {"timestamp":"Oct 24 13:00:00.875","level":"INFO","fields":{"message":"yak shaving completed","all_yaks_shaved":false},"target":"fmt_json"}
|
||||||
|
///
|
||||||
|
/// logfmt: human-readable and machine-parseable
|
||||||
|
///
|
||||||
|
/// level=info msg="This is an info message" target="logging" location="logfmt/tests/logging.rs:36" time=1612181556329599000
|
||||||
|
/// level=debug msg="This is a debug message" target="logging" location="logfmt/tests/logging.rs:37" time=1612181556329618000
|
||||||
|
/// level=trace msg="This is a trace message" target="logging" location="logfmt/tests/logging.rs:38" time=1612181556329634000
|
||||||
|
#[structopt(long = "--log-format", env = "LOG_FORMAT", default_value = "full", verbatim_doc_comment)]
|
||||||
|
pub log_format: LogFormat,
|
||||||
|
|
||||||
|
/// Tracing: exporter type
|
||||||
|
///
|
||||||
|
/// Can be one of: none, jaeger, otlp
|
||||||
|
///
|
||||||
|
/// When enabled, additional flags are considered (see flags related to OTLP
|
||||||
|
/// and Jaeger).
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter",
|
||||||
|
env = "TRACES_EXPORTER",
|
||||||
|
default_value = "none"
|
||||||
|
)]
|
||||||
|
pub traces_exporter: TracesExporter,
|
||||||
|
|
||||||
|
/// Tracing: filter directive
|
||||||
|
///
|
||||||
|
/// Configures traces severity level filter, by target.
|
||||||
|
///
|
||||||
|
/// Simplest options: error, warn, info, debug, trace
|
||||||
|
///
|
||||||
|
/// Levels for different modules can be specified. For example
|
||||||
|
/// `debug,hyper::proto::h1=info` specifies debug tracing for all modules
|
||||||
|
/// except for the `hyper::proto::h1` module which will only display info
|
||||||
|
/// level tracing.
|
||||||
|
///
|
||||||
|
/// Extended syntax provided by `tracing-subscriber` includes span/field
|
||||||
|
/// filters. See <https://docs.rs/tracing-subscriber/0.2.17/tracing_subscriber/filter/struct.EnvFilter.html> for more details.
|
||||||
|
///
|
||||||
|
/// No filter by default.
|
||||||
|
#[structopt(long = "--traces-filter", env = "TRACES_FILTER")]
|
||||||
|
pub traces_filter: Option<String>,
|
||||||
|
|
||||||
|
/// Tracing: sampler type
|
||||||
|
///
|
||||||
|
/// Can be one of:
|
||||||
|
/// always_on, always_off, traceidratio,
|
||||||
|
/// parentbased_always_on, parentbased_always_off, parentbased_traceidratio
|
||||||
|
///
|
||||||
|
/// These alternatives are described in detail at <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/sdk-environment-variables.md#general-sdk-configuration>.
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-sampler",
|
||||||
|
env = "TRACES_SAMPLER",
|
||||||
|
default_value = "parentbased_traceidratio"
|
||||||
|
)]
|
||||||
|
pub traces_sampler: TracesSampler,
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
/// Tracing: sampler argument
|
||||||
|
///
|
||||||
|
/// Valid range: [0.0, 1.0].
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-sampler` is set to
|
||||||
|
/// parentbased_traceidratio (default) or traceidratio.
|
||||||
|
///
|
||||||
|
/// With sample parentbased_traceidratio, the following rules apply:
|
||||||
|
/// - if parent is sampled, then all of its children are sampled
|
||||||
|
/// - else sample this portion of traces (0.5 = 50%)
|
||||||
|
///
|
||||||
|
/// More details about this sampling argument at <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/sdk-environment-variables.md#general-sdk-configuration>.
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-sampler-arg",
|
||||||
|
env = "TRACES_SAMPLER_ARG",
|
||||||
|
default_value = "1.0",
|
||||||
|
verbatim_doc_comment
|
||||||
|
)]
|
||||||
|
pub traces_sampler_arg: f64,
|
||||||
|
|
||||||
|
/// Tracing: OTLP (eg OpenTelemetry collector) network hostname
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "otlp".
|
||||||
|
///
|
||||||
|
/// Protocol is gRPC. HTTP is not supported.
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-otlp-host",
|
||||||
|
env = "TRACES_EXPORTER_OTLP_HOST",
|
||||||
|
default_value = "localhost"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_otlp_host: String,
|
||||||
|
|
||||||
|
/// Tracing: OTLP (eg OpenTelemetry collector) network port
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "otlp".
|
||||||
|
///
|
||||||
|
/// Protocol is gRPC. HTTP is not supported.
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-otlp-port",
|
||||||
|
env = "TRACES_EXPORTER_OTLP_PORT",
|
||||||
|
default_value = "4317"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_otlp_port: NonZeroU16,
|
||||||
|
|
||||||
|
/// Tracing: Jaeger agent network hostname
|
||||||
|
///
|
||||||
|
/// Protocol is Thrift/Compact over UDP.
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "jaeger".
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-jaeger-agent-host",
|
||||||
|
env = "TRACES_EXPORTER_JAEGER_AGENT_HOST",
|
||||||
|
default_value = "0.0.0.0"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_jaeger_agent_host: String,
|
||||||
|
|
||||||
|
/// Tracing: Jaeger agent network port
|
||||||
|
///
|
||||||
|
/// Protocol is Thrift/Compact over UDP.
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "jaeger".
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-jaeger-agent-port",
|
||||||
|
env = "TRACES_EXPORTER_JAEGER_AGENT_PORT",
|
||||||
|
default_value = "6831"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_jaeger_agent_port: NonZeroU16,
|
||||||
|
|
||||||
|
/// Tracing: Jaeger service name.
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "jaeger".
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-jaeger-service-name",
|
||||||
|
env = "TRACES_EXPORTER_JAEGER_SERVICE_NAME",
|
||||||
|
default_value = "iox-conductor"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_jaeger_service_name: String,
|
||||||
|
|
||||||
|
/// Tracing: Jaeger max UDP packet size
|
||||||
|
///
|
||||||
|
/// Default to 1300, which is a safe MTU.
|
||||||
|
///
|
||||||
|
/// You can increase it to 65000 if the target is a jaeger collector
|
||||||
|
/// on localhost. If so, the batching exporter will be enabled for
|
||||||
|
/// extra efficiency. Otherwise an UDP packet will be sent for each exported span.
|
||||||
|
///
|
||||||
|
/// Only used if `--traces-exporter` is "jaeger".
|
||||||
|
#[structopt(
|
||||||
|
long = "--traces-exporter-jaeger-max-packet-size",
|
||||||
|
env = "TRACES_EXPORTER_JAEGER_MAX_PACKET_SIZE",
|
||||||
|
default_value = "1300"
|
||||||
|
)]
|
||||||
|
pub traces_exporter_jaeger_max_packet_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TracingConfig {
|
||||||
|
pub fn to_builder(&self) -> Builder<BoxMakeWriter> {
|
||||||
|
Builder::new()
|
||||||
|
.with_log_filter(&self.log_filter)
|
||||||
|
// with_verbose_count goes after with_log_filter because our CLI flag state
|
||||||
|
// that --v overrides --log-filter.
|
||||||
|
.with_log_verbose_count(self.log_verbose_count)
|
||||||
|
.with_log_destination(self.log_destination)
|
||||||
|
.with_log_format(self.log_format)
|
||||||
|
.with_traces_filter(&self.traces_filter)
|
||||||
|
.with_traces_exporter(self.traces_exporter)
|
||||||
|
.with_traces_sampler(self.traces_sampler, self.traces_sampler_arg)
|
||||||
|
.with_jaeger_config(JaegerConfig {
|
||||||
|
agent_host: self.traces_exporter_jaeger_agent_host.clone(),
|
||||||
|
agent_port: self.traces_exporter_jaeger_agent_port,
|
||||||
|
service_name: self.traces_exporter_jaeger_service_name.clone(),
|
||||||
|
max_packet_size: self.traces_exporter_jaeger_max_packet_size,
|
||||||
|
})
|
||||||
|
.with_oltp_config(OtlpConfig {
|
||||||
|
host: self.traces_exporter_otlp_host.clone(),
|
||||||
|
port: self.traces_exporter_otlp_port,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_global_subscriber(&self) -> Result<TracingGuard> {
|
||||||
|
self.to_builder().install_global()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TracingConfig> for Builder<BoxMakeWriter> {
|
||||||
|
fn from(config: TracingConfig) -> Self {
|
||||||
|
config.to_builder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::test_util::simple_test;
|
||||||
|
|
||||||
|
fn to_vec(v: &[&str]) -> Vec<String> {
|
||||||
|
v.iter().map(|s| s.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log_verbose_count() {
|
||||||
|
let cfg = TracingConfig::from_iter_safe(to_vec(&["cli"])).unwrap();
|
||||||
|
assert_eq!(cfg.log_verbose_count, 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
simple_test(cfg.into()).without_timestamps(),
|
||||||
|
r#"
|
||||||
|
ERROR foo
|
||||||
|
WARN woo
|
||||||
|
"#
|
||||||
|
.trim_start(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cfg = TracingConfig::from_iter_safe(to_vec(&["cli", "-v"])).unwrap();
|
||||||
|
assert_eq!(cfg.log_verbose_count, 1);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
simple_test(cfg.into()).without_timestamps(),
|
||||||
|
r#"
|
||||||
|
ERROR foo
|
||||||
|
WARN woo
|
||||||
|
INFO bar
|
||||||
|
"#
|
||||||
|
.trim_start(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cfg = TracingConfig::from_iter_safe(to_vec(&["cli", "-vv"])).unwrap();
|
||||||
|
assert_eq!(cfg.log_verbose_count, 2);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
simple_test(cfg.into()).without_timestamps(),
|
||||||
|
r#"
|
||||||
|
ERROR foo
|
||||||
|
WARN woo
|
||||||
|
INFO bar
|
||||||
|
DEBUG baz
|
||||||
|
"#
|
||||||
|
.trim_start(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cfg = TracingConfig::from_iter_safe(to_vec(&["cli", "-vvv"])).unwrap();
|
||||||
|
assert_eq!(cfg.log_verbose_count, 3);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
simple_test(cfg.into()).without_timestamps(),
|
||||||
|
r#"
|
||||||
|
ERROR foo
|
||||||
|
WARN woo
|
||||||
|
INFO bar
|
||||||
|
DEBUG baz
|
||||||
|
TRACE trax
|
||||||
|
"#
|
||||||
|
.trim_start(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum LogFormat {
|
pub enum LogFormat {
|
||||||
Full,
|
Full,
|
||||||
|
@ -135,3 +137,15 @@ impl std::fmt::Display for TracesSampler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct JaegerConfig {
|
||||||
|
pub agent_host: String,
|
||||||
|
pub agent_port: NonZeroU16,
|
||||||
|
pub service_name: String,
|
||||||
|
pub max_packet_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OtlpConfig {
|
||||||
|
pub host: String,
|
||||||
|
pub port: NonZeroU16,
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
//! Log and trace initialization and setup
|
//! Log and trace initialization and setup
|
||||||
|
|
||||||
|
#[cfg(feature = "structopt")]
|
||||||
|
pub mod cli;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
|
|
||||||
use observability_deps::{
|
use observability_deps::{
|
||||||
|
@ -17,7 +20,6 @@ use observability_deps::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::num::NonZeroU16;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
@ -32,7 +34,7 @@ pub enum Error {
|
||||||
SetGlobalDefaultError(#[from] tracing::dispatcher::SetGlobalDefaultError),
|
SetGlobalDefaultError(#[from] tracing::dispatcher::SetGlobalDefaultError),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
/// Builder for tracing and logging.
|
/// Builder for tracing and logging.
|
||||||
pub struct Builder<W = fn() -> io::Stdout> {
|
pub struct Builder<W = fn() -> io::Stdout> {
|
||||||
|
@ -49,18 +51,6 @@ pub struct Builder<W = fn() -> io::Stdout> {
|
||||||
otlp_config: Option<OtlpConfig>,
|
otlp_config: Option<OtlpConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JaegerConfig {
|
|
||||||
pub agent_host: String,
|
|
||||||
pub agent_port: NonZeroU16,
|
|
||||||
pub service_name: String,
|
|
||||||
pub max_packet_size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OtlpConfig {
|
|
||||||
pub host: String,
|
|
||||||
pub port: NonZeroU16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Builder {
|
impl Default for Builder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -451,19 +441,19 @@ pub mod test_util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a test helper that creates a builder with a few test-friendly parameters
|
/// This is a test helper that sets a few test-friendly parameters
|
||||||
/// such as disabled ANSI escape sequences. The caller provides a function to further customize
|
/// such as disabled ANSI escape sequences on the provided builder.
|
||||||
/// the builder. This helper then emits a few logs of different verbosity levels and compares the output
|
/// This helper then emits a few logs of different verbosity levels
|
||||||
/// with the provided `want` string.
|
/// and returns the captured output.
|
||||||
pub fn simple_test<F, W>(setup: F, want: impl AsRef<str>)
|
pub fn simple_test<W>(builder: Builder<W>) -> Captured
|
||||||
where
|
where
|
||||||
F: FnOnce(Builder) -> Builder<W>,
|
|
||||||
W: MakeWriter + Send + Sync + 'static,
|
W: MakeWriter + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let (writer, output) = TestWriter::new();
|
let (writer, output) = TestWriter::new();
|
||||||
let builder = Builder::new().with_target(false).with_ansi(false);
|
let subscriber = builder
|
||||||
let subscriber = setup(builder)
|
|
||||||
.with_writer(writer)
|
.with_writer(writer)
|
||||||
|
.with_target(false)
|
||||||
|
.with_ansi(false)
|
||||||
.build()
|
.build()
|
||||||
.expect("subscriber");
|
.expect("subscriber");
|
||||||
|
|
||||||
|
@ -475,8 +465,7 @@ pub mod test_util {
|
||||||
trace!("trax");
|
trace!("trax");
|
||||||
});
|
});
|
||||||
|
|
||||||
let want = want.as_ref();
|
output
|
||||||
assert_eq!(output.without_timestamps(), want);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,8 +477,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_logging() {
|
fn simple_logging() {
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder,
|
simple_test(Builder::new()).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
ERROR foo
|
ERROR foo
|
||||||
WARN woo
|
WARN woo
|
||||||
|
@ -500,8 +489,8 @@ WARN woo
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_logging_logfmt() {
|
fn simple_logging_logfmt() {
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder.with_log_format(LogFormat::Logfmt),
|
simple_test(Builder::new().with_log_format(LogFormat::Logfmt)).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
level=error msg=foo
|
level=error msg=foo
|
||||||
level=warn msg=woo
|
level=warn msg=woo
|
||||||
|
@ -512,8 +501,8 @@ level=warn msg=woo
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verbose_count() {
|
fn verbose_count() {
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder.with_log_verbose_count(0),
|
simple_test(Builder::new().with_log_verbose_count(0)).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
ERROR foo
|
ERROR foo
|
||||||
WARN woo
|
WARN woo
|
||||||
|
@ -521,8 +510,8 @@ WARN woo
|
||||||
.trim_start(),
|
.trim_start(),
|
||||||
);
|
);
|
||||||
|
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder.with_log_verbose_count(1),
|
simple_test(Builder::new().with_log_verbose_count(1)).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
ERROR foo
|
ERROR foo
|
||||||
WARN woo
|
WARN woo
|
||||||
|
@ -531,8 +520,8 @@ INFO bar
|
||||||
.trim_start(),
|
.trim_start(),
|
||||||
);
|
);
|
||||||
|
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder.with_log_verbose_count(2),
|
simple_test(Builder::new().with_log_verbose_count(2)).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
ERROR foo
|
ERROR foo
|
||||||
WARN woo
|
WARN woo
|
||||||
|
@ -542,8 +531,8 @@ DEBUG baz
|
||||||
.trim_start(),
|
.trim_start(),
|
||||||
);
|
);
|
||||||
|
|
||||||
simple_test(
|
assert_eq!(
|
||||||
|builder| builder.with_log_verbose_count(3),
|
simple_test(Builder::new().with_log_verbose_count(3)).without_timestamps(),
|
||||||
r#"
|
r#"
|
||||||
ERROR foo
|
ERROR foo
|
||||||
WARN woo
|
WARN woo
|
||||||
|
|
Loading…
Reference in New Issue