Skip to content

Add Graylog logging configuration #252#264

Open
Alexj9837 wants to merge 10 commits intomainfrom
numtracker_graylog
Open

Add Graylog logging configuration #252#264
Alexj9837 wants to merge 10 commits intomainfrom
numtracker_graylog

Conversation

@Alexj9837
Copy link
Copy Markdown

#252

Summary

  • Add tracing-gelf layer to ship structured logs to a Graylog instance via GELF TCP
  • Expose --graylog <URL> and --logging-level <LEVEL> CLI flags (also via NUMTRACKER_GRAYLOG / NUMTRACKER_GRAYLOG_LOG_LEVEL env vars)
  • Add Graylog env var injection to Helm chart with numtracker.graylog.enabled/host/level values

@Alexj9837 Alexj9837 marked this pull request as ready for review March 16, 2026 15:59
Copy link
Copy Markdown
Contributor

@ZohebShaikh ZohebShaikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good only few minor things

src/logging.rs Outdated
Comment on lines +112 to +119
if errors.0.is_empty() {
error!("Failed to connect to graylog - address lookup failed");
} else {
warn!(
"Connection to graylog {:?} closed: {:?}",
handle.address(),
errors
);
Copy link
Copy Markdown
Contributor

@ZohebShaikh ZohebShaikh Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors.0 give Vec<SocketAddr,Error> docs

If there are errors you get a warning which I find difficult to understand
This is just a question to understand the code.

I don't know will it be very difficult to write unit test to check all the error cases

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gelf library is odd. There is a bit more explanation in the amygdala version here.

I'm not sure if the warn should also be an error but the no errors == error case caught me out as well so it might be worth including a similar comment here.

@Alexj9837 Alexj9837 force-pushed the numtracker_graylog branch from bda4336 to 831a990 Compare March 18, 2026 15:44
- Extract GraylogOptions into its own struct, independent of TracingOptions
- Remove .vscode/launch.json (to be added in a separate PR)
- Remove version/build additional_field calls from Graylog logger
- Switch let-else to if-let in init_graylog
- Warn when Graylog URL has no port rather than silently defaulting
- Add reconnect loop so dropped connections recover automatically
- Improve connection error messages to distinguish DNS failure from TCP errors
@Alexj9837 Alexj9837 force-pushed the numtracker_graylog branch from 831a990 to 0a86a82 Compare March 30, 2026 13:56
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 78.57143% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.53%. Comparing base (4d5553c) to head (9092648).

Files with missing lines Patch % Lines
src/logging.rs 68.25% 20 Missing ⚠️
src/main.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #264      +/-   ##
==========================================
+ Coverage   76.35%   76.53%   +0.18%     
==========================================
  Files          13       13              
  Lines        1937     2033      +96     
==========================================
+ Hits         1479     1556      +77     
- Misses        458      477      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Collaborator

@tpoliaw tpoliaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, thanks for looking at it. A couple of changes could make the error handling a bit cleaner and keep it more consistent with what is already there though.

pub graylog_url: Option<Url>,
/// The minimum level of logging events to send
#[clap(long, default_value_t = Level::INFO, env = "NUMTRACKER_GRAYLOG_LOG_LEVEL")]
pub logging_level: Level,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be graylog_level to mirror the tracing_level in TracingOptions

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense, I was in an isolated view of being inside "GraylogOptions" so logging level, but graylog_level gives more context

pub logging_level: Level,
}

impl GraylogOptions {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this impl be moved down to below the impl TracingOptions block to keep it more consistent?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this down.

/// Returns the `host:port` address string for connecting to Graylog, or `None` if no URL is
/// configured. Returns an error if the URL has no port — a missing port is always a
/// misconfiguration and should not be silently defaulted.
pub fn address(&self) -> Result<Option<String>, String> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mainly validation stuff so might be better served as a clap value_parser to generate better error messages and let the rest of the code assume things were valid.

On the other hand we're also ignoring the scheme and always using tcp in the logging init method.
Assuming we're sticking with tcp and the gelf library expects a string, I don't think we need to work with a URL here at all - accepting host and port might be clearer. It could be surprising to pass udp://graylog.example.com:1234 and for it to still use tcp without warning.

async fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::init();
let _ = logging::init(args.log_level(), args.tracing());
let _ = logging::init(args.log_level(), args.tracing(), &args.graylog);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably handle errors here. Currently if any log setup fails you get no output and debugging why is a pain.

openidconnect = { version = "4.0.0", optional = true }
toml = { version = "1.0.0", optional = true }
tracing-gelf = "0.9.0"
thiserror = "2.0.18"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bringing in thiserror for a single error type might be an overkill. Implementing the error manually is not too much boilerplate.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing thiserror.

trackers/

# Local IDE settings
.vscode/settings.json
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be part of this PR - it's a good change but can go in at the same time as the launch.json, maybe as .vscode/ and then manually add the specific files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing it.

src/logging.rs Outdated
Comment on lines +112 to +119
if errors.0.is_empty() {
error!("Failed to connect to graylog - address lookup failed");
} else {
warn!(
"Connection to graylog {:?} closed: {:?}",
handle.address(),
errors
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gelf library is odd. There is a bit more explanation in the amygdala version here.

I'm not sure if the warn should also be an error but the no errors == error case caught me out as well so it might be worth including a similar comment here.

#[error("Exporter build error: {0}")]
Exporter(#[from] ExporterBuildError),
#[error("Graylog error: {0}")]
Graylog(#[from] std::io::Error),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Graylog variant ever used?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants