Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@
```
-->

## v10.11.20260806 _[unreleased; planned for 2026-08-06]_

Check failure on line 38 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / changelog

Missing next release header

Missing unreleased changelog heading for v10.11.20260813. Add this block: ## v10.11.20260813 _[unreleased; planned for 2026-08-13]_ ### Changed - **amaru-AREA**: short description ([#123][]) Optional longer description. [#123]: https://github.com/pragma-org/amaru/pull/123

Check failure on line 38 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / changelog

Wrong top release

Latest changelog entry must be v10.11.20260813; found v10.11.20260806. Add or update the top release header for the upcoming release.

### Added

- **amaru**: log precise build identity (package version, full git commit, dirty flag, OS/arch) at INFO after tracing is set up, so operator log files identify the running binary. ([#1161](https://github.com/pragma-org/amaru/issues/1161))
- **amaru**: add `amaru node rollback` to recover after a wrongly invalidated block or to rewind to an epoch start. Supports `--immutable-tip` (chain store only) and `--epoch` (ledger snapshot reset + chain realign). Clears all descendant validation flags, sets the anchor/best tip, and culls the best-chain fragment. ([#1072](https://github.com/pragma-org/amaru/issues/1072))
- **amaru**: add `amaru mithril sync` to download verified Mithril immutable files and replay their blocks directly into the chain and ledger stores.
- **amaru**: add `amaru node rm --wipe-all-dbs` to remove the ledger and chain databases resolved from the selected network.
Expand Down
10 changes: 10 additions & 0 deletions crates/amaru-observability/src/schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,16 @@ define_schemas! {
required with_colors: bool
}
}
build {
/// Running binary build/version identity (package version, git commit, target).
public VERSION {
required version: String
required git_commit: String
required git_dirty: bool
required os: String
required arch: String
}
}
trace {
/// Resolution of a trace filter from the environment
public FILTER {
Expand Down
7 changes: 5 additions & 2 deletions crates/amaru/src/bin/amaru/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,16 @@ fn try_main() -> Result<(), Box<dyn Error>> {
} else {
// OpenTelemetry batch exporters require a current Tokio runtime.
let _enter = rt.enter();
setup_observability(
let result = setup_observability(
with_open_telemetry,
with_json_traces,
color_enabled,
&ListenAddressHint(listen_address.as_deref()),
tui.as_ref().map(tui::Session::layer),
)
);
// Record precise binary identity in operator logs as soon as tracing is live.
version::log_build_version();
result
};

let result = runnable.run_on(&rt, &signals, metrics);
Expand Down
97 changes: 97 additions & 0 deletions crates/amaru/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

use std::sync::LazyLock;

use amaru_observability::info;

mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
Expand Down Expand Up @@ -43,6 +45,10 @@ pub fn display_version() -> &'static str {
DISPLAY_VERSION.as_str()
}

pub fn git_commit_hash() -> Option<&'static str> {
built_info::GIT_COMMIT_HASH
}

pub fn git_commit_hash_short() -> Option<&'static str> {
built_info::GIT_COMMIT_HASH_SHORT
}
Expand All @@ -58,3 +64,94 @@ pub fn target_os() -> &'static str {
pub fn target_arch() -> &'static str {
built_info::CFG_TARGET_ARCH
}

/// Emit a structured INFO event with the running binary's version and git identity.
///
/// Call this once after the tracing subscriber is installed so operator log files
/// record which build produced them.
pub fn log_build_version() {
info!(
setup::build::VERSION,
version = package_version(),
git_commit = git_commit_hash().unwrap_or("unknown"),
git_dirty = git_dirty().unwrap_or(false),
os = target_os(),
arch = target_arch(),
);
}

#[cfg(test)]
mod tests {
use std::{
io::{self, Write},
sync::{Arc, Mutex},
};

use tracing_subscriber::fmt::MakeWriter;

use super::*;

/// Captures fmt layer output so tests can assert on emitted events.
#[derive(Clone, Default)]
struct CaptureWriter {
buffer: Arc<Mutex<Vec<u8>>>,
}

impl CaptureWriter {
fn contents(&self) -> String {
let bytes = self.buffer.lock().expect("capture buffer lock").clone();
String::from_utf8_lossy(&bytes).into_owned()
}
}

impl Write for CaptureWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buffer.lock().expect("capture buffer lock").write(buf)
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

impl<'a> MakeWriter<'a> for CaptureWriter {
type Writer = CaptureWriter;

fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}

#[test]
fn log_build_version_emits_package_and_git_fields() {
let writer = CaptureWriter::default();
let subscriber = tracing_subscriber::fmt()
.with_writer(writer.clone())
// Equivalent to with_test_writer for cargo test visibility, but captureable for asserts.
.with_max_level(tracing::Level::INFO)
.with_target(true)
.with_level(true)
.finish();

tracing::subscriber::with_default(subscriber, || {
log_build_version();
});

let output = writer.contents();

assert!(
output.contains("amaru::setup") && output.contains("build.version"),
"expected amaru::setup build.version event target in output:\n{output}"
);
assert!(
output.contains(package_version()),
"expected package version {} in output:\n{output}",
package_version()
);
assert!(output.contains(target_os()), "expected os {} in output:\n{output}", target_os());
assert!(output.contains(target_arch()), "expected arch {} in output:\n{output}", target_arch());

let expected_commit = git_commit_hash().unwrap_or("unknown");
assert!(output.contains(expected_commit), "expected git commit {expected_commit} in output:\n{output}");
}
}
18 changes: 18 additions & 0 deletions docs/TRACES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1846,6 +1846,24 @@ For information on how to use and filter these spans, see [monitoring/README.md]

</details>

## target: `amaru::setup::build`

| name | level | public | description | required fields | optional fields |
| --- | --- | --- | --- | --- | --- |
| `version` | `TRACE` | public | Running binary build/version identity (package version, git commit, target). | version, git_commit, git_dirty, os, arch | |

<details><summary>span: `version`</summary>

| field | type | required |
| --- | --- | --- |
| `version` | `string` | ✓ |
| `git_commit` | `string` | ✓ |
| `git_dirty` | `boolean` | ✓ |
| `os` | `string` | ✓ |
| `arch` | `string` | ✓ |

</details>

## target: `amaru::setup::observability`

| name | level | public | description | required fields | optional fields |
Expand Down
34 changes: 34 additions & 0 deletions docs/traces-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3915,6 +3915,40 @@
"description": "A connection has been terminated (graceful disconnect, error, handshake refusal, or network error).",
"public": true
},
"amaru::setup::build::VERSION": {
"type": "object",
"properties": {
"version": {
"type": "string"
},
"git_commit": {
"type": "string"
},
"git_dirty": {
"type": "boolean"
},
"os": {
"type": "string"
},
"arch": {
"type": "string"
}
},
"required": [
"version",
"git_commit",
"git_dirty",
"os",
"arch"
],
"optional": [],
"additionalProperties": false,
"name": "version",
"level": "TRACE",
"target": "amaru::setup::build",
"description": "Running binary build/version identity (package version, git commit, target).",
"public": true
},
"amaru::setup::observability::INIT": {
"type": "object",
"properties": {
Expand Down
Loading