Skip to content

Commit e7c8ee2

Browse files
refi64sjoerdsimons
authored andcommitted
Introduce a custom logging system that forwards to GitLab
Instead of hardcoding the dependence on gitlab-runner's outputln, a custom macro is used with a unique field name. This field is picked up by a rather cursed bridge layer rewrites the events to instead use the fields gitlab-runner wants.
1 parent 7f6acba commit e7c8ee2

File tree

7 files changed

+177
-14
lines changed

7 files changed

+177
-14
lines changed

src/build_meta.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::{collections::HashMap, time::Duration};
22

33
use color_eyre::eyre::{Result, WrapErr};
4-
use gitlab_runner::outputln;
54
use open_build_service_api as obs;
65
use serde::{Deserialize, Serialize};
76
use tracing::{Instrument, debug, info_span, instrument};
87

9-
use crate::retry_request;
8+
use crate::{outputln, retry_request};
109

1110
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)]
1211
pub struct RepoArch {

src/handler.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use futures_util::StreamExt;
1313
use gitlab_runner::{
1414
JobHandler, JobResult, Phase, UploadableFile,
1515
job::{Dependency, Job, Variable},
16-
outputln,
1716
};
1817
use open_build_service_api as obs;
1918
use serde::{Deserialize, Serialize};
@@ -38,6 +37,7 @@ use crate::{
3837
RepoArch,
3938
},
4039
monitor::{MonitoredPackage, ObsMonitor, PackageCompletion, PackageMonitoringOptions},
40+
outputln,
4141
pipeline::{GeneratePipelineOptions, PipelineDownloadBinaries, generate_monitor_pipeline},
4242
prune::prune_branch,
4343
retry_request,
@@ -798,7 +798,7 @@ mod tests {
798798
use tracing_subscriber::{Layer, Registry, filter::Targets, prelude::*};
799799
use zip::ZipArchive;
800800

801-
use crate::{test_support::*, upload::compute_md5};
801+
use crate::{logging::GitLabForwarder, test_support::*, upload::compute_md5};
802802

803803
use super::*;
804804

@@ -866,7 +866,7 @@ mod tests {
866866
),
867867
)
868868
.with(tracing_error::ErrorLayer::default())
869-
.with(layer),
869+
.with(GitLabForwarder::new(layer)),
870870
)
871871
.await
872872
}

src/logging.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use gitlab_runner::GitlabLayer;
2+
use tracing::{
3+
Event, Level, Metadata, Subscriber,
4+
field::{self, Field, FieldSet},
5+
span::{Attributes, Id},
6+
subscriber::Interest,
7+
};
8+
use tracing_subscriber::{
9+
Layer,
10+
filter::{Filtered, Targets},
11+
layer::{Context, Filter},
12+
registry::LookupSpan,
13+
};
14+
15+
struct OutputTester(bool);
16+
17+
impl field::Visit for OutputTester {
18+
fn record_bool(&mut self, field: &field::Field, value: bool) {
19+
if field.name() == "obs_gitlab_runner.output" {
20+
self.0 = value
21+
}
22+
}
23+
24+
fn record_debug(&mut self, _field: &field::Field, _value: &dyn std::fmt::Debug) {}
25+
}
26+
27+
struct MessageExtractor(Option<String>);
28+
29+
impl field::Visit for MessageExtractor {
30+
fn record_str(&mut self, field: &Field, value: &str) {
31+
if field.name() == "message" {
32+
self.0 = Some(value.to_owned());
33+
}
34+
}
35+
36+
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
37+
if field.name() == "message" {
38+
self.0 = Some(format!("{value:?}"));
39+
}
40+
}
41+
}
42+
43+
// This mostly wraps a standard GitlabLayer, but it bypasses the filter to pass
44+
// through any events with `obs_gitlab_runner.output` set, rewriting them to
45+
// instead use `gitlab.output`.
46+
pub struct GitLabForwarder<S: Subscriber, F: Filter<S>>(Filtered<GitlabLayer, F, S>);
47+
48+
impl<S: Subscriber + Send + Sync + 'static + for<'span> LookupSpan<'span>, F: Filter<S> + 'static>
49+
GitLabForwarder<S, F>
50+
{
51+
pub fn new(inner: Filtered<GitlabLayer, F, S>) -> Filtered<Self, Targets, S> {
52+
GitLabForwarder(inner).with_filter(Targets::new().with_targets([
53+
("obs_gitlab_runner", Level::TRACE),
54+
// This target is used to inject the current job ID, which
55+
// gitlab-runner needs to actually send the logs out.
56+
("gitlab_runner::gitlab::job", Level::ERROR),
57+
]))
58+
}
59+
}
60+
61+
impl<S: Subscriber + Send + Sync + 'static + for<'span> LookupSpan<'span>, F: Filter<S> + 'static>
62+
Layer<S> for GitLabForwarder<S, F>
63+
{
64+
fn on_register_dispatch(&self, subscriber: &tracing::Dispatch) {
65+
self.0.on_register_dispatch(subscriber);
66+
}
67+
68+
fn on_layer(&mut self, subscriber: &mut S) {
69+
self.0.on_layer(subscriber);
70+
}
71+
72+
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
73+
self.0.register_callsite(metadata)
74+
}
75+
76+
fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
77+
// This controls *global* event filtering, not local, so the inner filter
78+
// should always return `true`. But we need to call it anyway, because
79+
// `Filter` will *save internal state* that's needed for other API
80+
// calls, and thus otherwise the event will always be treated as
81+
// disabled. (Of course, events in the span we want to forward will
82+
// also be disabled by this, which is why bypassing the filter in
83+
// `on_event` is important.)
84+
let enabled = self.0.enabled(metadata, ctx.clone());
85+
assert!(enabled);
86+
true
87+
}
88+
89+
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
90+
self.0.event_enabled(event, ctx)
91+
}
92+
93+
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
94+
self.0.on_new_span(attrs, id, ctx);
95+
}
96+
97+
fn on_follows_from(&self, span: &Id, follows: &Id, ctx: Context<'_, S>) {
98+
self.0.on_follows_from(span, follows, ctx);
99+
}
100+
101+
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
102+
let mut visitor = OutputTester(false);
103+
event.record(&mut visitor);
104+
if !visitor.0 {
105+
// No special behavior needed, so just forward it as-is.
106+
self.0.on_event(event, ctx);
107+
return;
108+
}
109+
110+
let mut visitor = MessageExtractor(None);
111+
event.record(&mut visitor);
112+
let Some(message) = visitor.0 else {
113+
return;
114+
};
115+
116+
// Create a new event that contains the fields needed for gitlab-runner.
117+
let fields = FieldSet::new(&["gitlab.output", "message"], event.metadata().callsite());
118+
let mut iter = fields.iter();
119+
let values = [
120+
// "gitlab.output = true"
121+
(&iter.next().unwrap(), Some(&true as &dyn tracing::Value)),
122+
// "message"
123+
(&iter.next().unwrap(), Some(&message as &dyn tracing::Value)),
124+
];
125+
126+
let value_set = fields.value_set(&values);
127+
128+
let event = if event.is_contextual() {
129+
// This event's parent is None, but if that's given to new_child_of,
130+
// then this will be treated as an event at the *root*, i.e.
131+
// completely parentless. By using `Event::new`, another contextual
132+
// event will be created, which can still be tied to the correct
133+
// `event_span`.
134+
Event::new(event.metadata(), &value_set)
135+
} else {
136+
Event::new_child_of(event.parent().cloned(), event.metadata(), &value_set)
137+
};
138+
139+
// Bypass the filter completely, because the event was almost certainly
140+
// filtered out in its `enabled` due to lacking `gitlab.output`.
141+
self.0.inner().on_event(&event, ctx);
142+
}
143+
144+
fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
145+
self.0.on_enter(id, ctx)
146+
}
147+
148+
fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
149+
self.0.on_exit(id, ctx)
150+
}
151+
152+
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
153+
self.0.on_close(id, ctx)
154+
}
155+
156+
fn on_id_change(&self, old: &Id, new: &Id, ctx: Context<'_, S>) {
157+
self.0.on_id_change(old, new, ctx);
158+
}
159+
}
160+
161+
#[macro_export]
162+
macro_rules! outputln {
163+
($($args:tt)*) => {
164+
::tracing::trace!(obs_gitlab_runner.output = true, $($args)*)
165+
};
166+
}

src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{fmt, str::FromStr, sync::Arc};
33
use clap::Parser;
44
use color_eyre::eyre::Result;
55
use gitlab_runner::{GitlabLayer, RunnerBuilder};
6+
use logging::GitLabForwarder;
67
use strum::{Display, EnumString};
78
use tracing::{Subscriber, error, info};
89
use tracing_subscriber::{
@@ -22,6 +23,7 @@ mod binaries;
2223
mod build_meta;
2324
mod dsc;
2425
mod handler;
26+
mod logging;
2527
mod monitor;
2628
mod pipeline;
2729
mod prune;
@@ -121,7 +123,7 @@ async fn main() {
121123

122124
let registry = tracing_subscriber::registry()
123125
.with(tracing_error::ErrorLayer::default())
124-
.with(layer);
126+
.with(GitLabForwarder::new(layer));
125127

126128
match args.log_format {
127129
LogFormat::Compact => registry

src/monitor.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ use std::time::Duration;
33
use color_eyre::eyre::{Context, Report, Result, ensure, eyre};
44
use derivative::*;
55
use futures_util::stream::StreamExt;
6-
use gitlab_runner::outputln;
76
use open_build_service_api as obs;
87
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
98
use tracing::{debug, instrument};
109

1110
use crate::{
1211
artifacts::{ArtifactDirectory, ArtifactReader, ArtifactWriter},
13-
retry_request,
12+
outputln, retry_request,
1413
};
1514

1615
#[derive(Debug)]

src/prune.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use color_eyre::eyre::{Context, Result, ensure};
2-
use gitlab_runner::outputln;
32
use open_build_service_api as obs;
4-
use tracing::info;
53

6-
use crate::retry_request;
4+
use crate::{outputln, retry_request};
75

86
async fn is_originally_branched(
97
client: &obs::Client,
@@ -49,7 +47,7 @@ pub async fn prune_branch(
4947

5048
if let Some(expected_rev) = expected_rev {
5149
if dir.rev.as_deref() != Some(expected_rev) {
52-
info!(
50+
outputln!(
5351
"Latest revision is {}, skipping prune",
5452
dir.rev.as_deref().unwrap_or("[unknown]")
5553
);

src/upload.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ use std::collections::HashMap;
33
use camino::{Utf8Path, Utf8PathBuf};
44
use color_eyre::eyre::{Context, Result, ensure, eyre};
55
use derivative::*;
6-
use gitlab_runner::outputln;
76
use md5::{Digest, Md5};
87
use open_build_service_api as obs;
98
use tracing::{Instrument, debug, info_span, instrument, trace};
109

1110
use crate::{
1211
artifacts::ArtifactDirectory,
1312
dsc::{Dsc, discard_pgp},
14-
retry_request,
13+
outputln, retry_request,
1514
};
1615

1716
type Md5String = String;

0 commit comments

Comments
 (0)