Skip to content

Commit 87ac7a8

Browse files
authored
Merge pull request #878 from Mossaka/logger
sandbox/cli: add logger to the container process
2 parents ed9a72c + 72ed58c commit 87ac7a8

File tree

11 files changed

+442
-17
lines changed

11 files changed

+442
-17
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/containerd-shim-wasm/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ opentelemetry_sdk = { version = "0.23", default-features = false, features = [
6666
], optional = true }
6767
tracing-opentelemetry = { version = "0.24", optional = true }
6868

69+
# vendored code
70+
time = { version = "0.3.29", features = ["serde", "std", "formatting"] }
6971

7072
[target.'cfg(unix)'.dependencies]
7173
zygote = { version = "0.1.2" }
@@ -86,6 +88,8 @@ windows-sys = { workspace = true, features = [
8688
"Win32_Foundation",
8789
"Win32_Storage_FileSystem",
8890
] }
91+
# vendored code
92+
mio = { version = "1.0", features = ["os-ext", "os-poll"] }
8993

9094
[build-dependencies]
9195
ttrpc-codegen = { version = "0.4.2" }

crates/containerd-shim-wasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
pub mod container;
77
pub mod sandbox;
8+
mod vendor;
89

910
#[cfg_attr(unix, path = "sys/unix/mod.rs")]
1011
#[cfg_attr(windows, path = "sys/windows/mod.rs")]

crates/containerd-shim-wasm/src/sandbox/cli.rs

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ fn log_mem() {
152152
log::info!("Zygote peak memory usage was: peak resident set {rss} kB, peak total {tot} kB");
153153
}
154154

155+
#[cfg(unix)]
156+
fn init_zygote_and_logger(debug: bool, config: &Config) {
157+
zygote::Zygote::init();
158+
if config.no_setup_logger {
159+
return;
160+
}
161+
zygote::Zygote::global().run(
162+
|(debug, default_log_level)| {
163+
// last two arguments are unused in unix
164+
crate::vendor::containerd_shim::logger::init(debug, &default_log_level, "", "")
165+
.expect("Failed to initialize logger");
166+
},
167+
(debug, config.default_log_level.clone()),
168+
);
169+
}
170+
155171
/// Main entry point for the shim.
156172
///
157173
/// If the `opentelemetry` feature is enabled, this function will start the shim with OpenTelemetry tracing.
@@ -166,8 +182,30 @@ pub fn shim_main<'a, I>(
166182
) where
167183
I: 'static + Instance + Sync + Send,
168184
{
185+
// parse the version flag
186+
let os_args: Vec<_> = std::env::args_os().collect();
187+
188+
let flags = parse(&os_args[1..]).unwrap();
189+
let argv0 = PathBuf::from(&os_args[0]);
190+
let argv0 = argv0.file_stem().unwrap_or_default().to_string_lossy();
191+
192+
if flags.version {
193+
println!("{argv0}:");
194+
println!(" Runtime: {name}");
195+
println!(" Version: {version}");
196+
println!(" Revision: {}", revision.into().unwrap_or("<none>"));
197+
println!();
198+
199+
std::process::exit(0);
200+
}
201+
202+
// Initialize the zygote and logger for the container process
169203
#[cfg(unix)]
170-
zygote::Zygote::init();
204+
{
205+
let default_config = Config::default();
206+
let config = config.as_ref().unwrap_or(&default_config);
207+
init_zygote_and_logger(flags.debug, config);
208+
}
171209

172210
#[cfg(feature = "opentelemetry")]
173211
if otel_traces_enabled() {
@@ -220,24 +258,8 @@ fn shim_main_inner<'a, I>(
220258
}
221259
}
222260
}
223-
let os_args: Vec<_> = std::env::args_os().collect();
224-
225-
let flags = parse(&os_args[1..]).unwrap();
226-
let argv0 = PathBuf::from(&os_args[0]);
227-
let argv0 = argv0.file_stem().unwrap_or_default().to_string_lossy();
228-
229-
if flags.version {
230-
println!("{argv0}:");
231-
println!(" Runtime: {name}");
232-
println!(" Version: {version}");
233-
println!(" Revision: {}", revision.into().unwrap_or("<none>"));
234-
println!();
235-
236-
std::process::exit(0);
237-
}
238261

239262
let shim_version = shim_version.into().unwrap_or("v1");
240-
241263
let lower_name = name.to_lowercase();
242264
let shim_id = format!("io.containerd.{lower_name}.{shim_version}");
243265

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Vendored Code
2+
3+
This directory contains vendored code from dependencies that we need to customize or extend.
4+
5+
### Usage
6+
7+
To use the vendored logger instead of the original:
8+
9+
```rust
10+
// Instead of
11+
use containerd_shim::logger;
12+
13+
// Use the vendored version
14+
use crate::vendor::containerd_shim::logger;
15+
```
16+
17+
### Updating Vendored Code
18+
19+
When a new version of `containerd-shim` is released, delete the vendored code and use the new version directly.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
//! VENDORED CODE FROM containerd-shim v0.8.0
18+
//!
19+
//! This file is vendored from the containerd-shim crate and should be replaced
20+
//! with the upstream version when a new release is available.
21+
//!
22+
//! Source: https://github.com/containerd/rust-extensions/blob/main/crates/containerd-shim/src/logger.rs
23+
24+
use std::borrow::BorrowMut;
25+
use std::fmt::Write as fmtwrite;
26+
use std::fs::{File, OpenOptions};
27+
use std::io::{self, Write};
28+
use std::path::Path;
29+
use std::str::FromStr;
30+
use std::sync::Mutex;
31+
32+
use containerd_shim::error::Error;
33+
use log::kv::{self, Visitor};
34+
use log::{Metadata, Record};
35+
use time::OffsetDateTime;
36+
use time::format_description::well_known::Rfc3339;
37+
38+
#[cfg(windows)]
39+
use crate::vendor::containerd_shim::sys::windows::NamedPipeLogger;
40+
41+
pub const LOG_ENV: &str = "RUST_LOG";
42+
43+
pub struct FifoLogger {
44+
file: Mutex<File>,
45+
}
46+
47+
impl FifoLogger {
48+
#[allow(dead_code)]
49+
pub fn new() -> Result<FifoLogger, io::Error> {
50+
Self::with_path("log")
51+
}
52+
53+
#[allow(dead_code)]
54+
pub fn with_path<P: AsRef<Path>>(path: P) -> Result<FifoLogger, io::Error> {
55+
let f = OpenOptions::new()
56+
.write(true)
57+
.read(false)
58+
.create(false)
59+
.open(path)?;
60+
61+
Ok(FifoLogger {
62+
file: Mutex::new(f),
63+
})
64+
}
65+
}
66+
67+
pub(crate) struct SimpleWriteVisitor {
68+
key_values: String,
69+
}
70+
71+
impl<'kvs> Visitor<'kvs> for SimpleWriteVisitor {
72+
fn visit_pair(&mut self, k: kv::Key<'kvs>, v: kv::Value<'kvs>) -> Result<(), kv::Error> {
73+
write!(&mut self.key_values, " {}=\"{}\"", k, v)?;
74+
Ok(())
75+
}
76+
}
77+
78+
impl SimpleWriteVisitor {
79+
pub(crate) fn new() -> SimpleWriteVisitor {
80+
SimpleWriteVisitor {
81+
key_values: String::new(),
82+
}
83+
}
84+
85+
pub(crate) fn as_str(&self) -> &str {
86+
&self.key_values
87+
}
88+
}
89+
90+
impl log::Log for FifoLogger {
91+
fn enabled(&self, metadata: &Metadata) -> bool {
92+
metadata.level() <= log::max_level()
93+
}
94+
95+
fn log(&self, record: &Record) {
96+
if self.enabled(record.metadata()) {
97+
let mut guard = self.file.lock().unwrap();
98+
99+
// collect key_values but don't fail if error parsing
100+
let mut writer = SimpleWriteVisitor::new();
101+
let _ = record.key_values().visit(&mut writer);
102+
103+
// The logger server may have temporarily shutdown, ignore the error instead of panic.
104+
//
105+
// Manual for pipe/FIFO: https://man7.org/linux/man-pages/man7/pipe.7.html
106+
// If all file descriptors referring to the read end of a pipe have been closed, then
107+
// a write(2) will cause a SIGPIPE signal to be generated for the calling process.
108+
// If the calling process is ignoring this signal, then write(2) fails with the error
109+
// EPIPE.
110+
let _ = writeln!(
111+
guard.borrow_mut(),
112+
"time=\"{}\" level={}{} msg=\"{}\"\n",
113+
rfc3339_formatted(),
114+
record.level().as_str().to_lowercase(),
115+
writer.as_str(),
116+
record.args()
117+
);
118+
}
119+
}
120+
121+
fn flush(&self) {
122+
// The logger server may have temporarily shutdown, ignore the error instead of panic.
123+
let _ = self.file.lock().unwrap().sync_all();
124+
}
125+
}
126+
127+
pub fn init(
128+
debug: bool,
129+
default_log_level: &str,
130+
_namespace: &str,
131+
_id: &str,
132+
) -> Result<(), Error> {
133+
#[cfg(unix)]
134+
let logger =
135+
FifoLogger::new().map_err(containerd_shim::io_error!(e, "failed to init logger"))?;
136+
137+
// Containerd on windows expects the log to be a named pipe in the format of \\.\pipe\containerd-<namespace>-<id>-log
138+
// There is an assumption that there is always only one client connected which is containerd.
139+
// If there is a restart of containerd then logs during that time period will be lost.
140+
//
141+
// https://github.com/containerd/containerd/blob/v1.7.0/runtime/v2/shim_windows.go#L77
142+
// https://github.com/microsoft/hcsshim/blob/5871d0c4436f131c377655a3eb09fc9b5065f11d/cmd/containerd-shim-runhcs-v1/serve.go#L132-L137
143+
#[cfg(windows)]
144+
let logger = NamedPipeLogger::new(_namespace, _id)
145+
.map_err(containerd_shim::io_error!(e, "failed to init logger"))?;
146+
147+
configure_logging_level(debug, default_log_level);
148+
log::set_boxed_logger(Box::new(logger))?;
149+
Ok(())
150+
}
151+
152+
fn configure_logging_level(debug: bool, default_log_level: &str) {
153+
let debug_level = std::env::var(LOG_ENV).unwrap_or(default_log_level.to_string());
154+
let debug_level = log::LevelFilter::from_str(&debug_level).unwrap_or(log::LevelFilter::Info);
155+
let level = if debug && log::LevelFilter::Debug > debug_level {
156+
log::LevelFilter::Debug
157+
} else {
158+
debug_level
159+
};
160+
log::set_max_level(level);
161+
}
162+
163+
pub(crate) fn rfc3339_formatted() -> String {
164+
OffsetDateTime::now_utc()
165+
.format(&Rfc3339)
166+
.unwrap_or(OffsetDateTime::now_utc().to_string())
167+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
//! VENDORED CODE FROM containerd-shim v0.8.0
18+
//!
19+
//! This module contains vendored code from the containerd-shim crate.
20+
//! It should be replaced with the upstream version when a new release is available.
21+
//!
22+
//! Source: https://github.com/containerd/rust-extensions/tree/shim-v0.8.0/crates/shim
23+
24+
pub mod logger;
25+
mod sys;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#[cfg(windows)]
18+
pub(crate) mod windows;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
pub(crate) mod named_pipe_logger;
17+
pub use named_pipe_logger::NamedPipeLogger;

0 commit comments

Comments
 (0)