1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! The timestamp every line in a sheep's or a dog's log file carries, and
//! how a reader takes it back off.
//!
//! # Why this is shared rather than the daemon's own
//!
//! It is a contract between one writer and several readers. The daemon
//! writes the stamp; `shep bleats --no-follow` reads it back off a file, and
//! so does `shep lookout`'s tail pane, and so does the `whistle` tool a
//! model calls. A format defined in the daemon and re-spelled in each reader
//! is a format that drifts, and the failure would be silent: a reader whose
//! idea of the width is one character out returns lines with a stray digit
//! at the front and nothing raises an error.
//!
//! # Why the stamp is on the file and not on the line
//!
//! A log file that carries no time is a file an operator cannot date. The
//! incident this exists for was a 341 KB log of byte-identical lines whose
//! newest entry was two days old, read by someone who had no way to see that
//! and who spent two days acting on it as if it were live. `mtime` answers
//! only for the whole file, and only until something touches it — shep's own
//! rotation touches it — so the answer has to live on the line.
//!
//! What it must NOT do is change what a sheep is reported to have said.
//! `Bus::publish_log` carries a sheep's line verbatim, so `shep bleats
//! --follow` and every dog subscribed to `log.*` see the sheep's own bytes;
//! [`strip`] is what keeps the file readers agreeing with them. `line` in
//! `bleats --format json` therefore means the same thing on both paths, and
//! means what it always did.
use Write as _;
/// The `strftime` spelling of the stamp: `2026-09-02T14:22:31.412+02:00`.
///
/// **Local time**, because the reader is a person looking at their own
/// clock — the same call `shep list`'s table already makes for the
/// timestamps it prints. **With the offset**, because that is what keeps
/// local time from being a lie: a bare one is unreadable across a DST
/// boundary and unusable to anyone correlating this file against a UTC one,
/// and printing it costs six characters. **RFC 3339**, because it sorts
/// lexicographically within one offset and every log tool already parses it.
/// **Milliseconds**, because a dog's handshake round trip is measured in
/// them, so a second-resolution stamp would put a spawn, its handshake and
/// its refusal all at the same instant.
pub const LOG_TIMESTAMP_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.3f%:z";
/// How many bytes [`stamp_into`] writes — the stamp and the single space
/// that separates it from the line.
///
/// Fixed rather than approximate, and that is a property of
/// [`LOG_TIMESTAMP_FORMAT`] rather than a hope: `%:z` renders `+HH:MM` for
/// every offset chrono can produce (it truncates the sub-minute offsets a
/// handful of pre-1900 zones carry), and every other field in that format is
/// zero-padded to a fixed width. 10 date + 1 `T` + 8 time + 4 `.mmm` + 6
/// offset + 1 space.
pub const LOG_STAMP_BYTES: usize = 30;
/// Appends the current local time in [`LOG_TIMESTAMP_FORMAT`], plus the
/// separating space, to `buf`.
///
/// Takes a buffer rather than returning a `String` because the caller on the
/// hot path runs once per logged line and can reuse one allocation for the
/// life of a log file — a sheep emitting 1.6M lines a second is a workload
/// shep's log pump has actually been measured against.
///
/// # Panics
///
/// Debug builds only, and only if [`LOG_TIMESTAMP_FORMAT`] is edited into a
/// width [`LOG_STAMP_BYTES`] no longer describes. Every reader strips the
/// prefix by that count, so an edit that changed it would otherwise be found
/// by a reader's mangled output rather than by a test run.
/// `line` with its stamp removed, or `line` unchanged if it does not carry
/// one.
///
/// The stamp is RECOGNISED rather than assumed, by parsing the first
/// [`LOG_STAMP_BYTES`] as RFC 3339. Blindly cutting a fixed prefix would be
/// cheaper and is wrong twice over: a log file predating this format, or one
/// an operator's own tooling appended to, would lose the first 30 characters
/// of every line — and a rotated archive holding both is a file readers have
/// to handle, since nothing rewrites what is already on disk.
///
/// Cheap on the common path: the shape test rejects a line that is too short
/// or has no space where the separator belongs before any parsing happens.