1use chrono::{
2 SecondsFormat,
3 format::{DelayedFormat, StrftimeItems},
4 prelude::Local,
5};
6use rand::{Rng, rng};
7
8static FAKE_USERNAMES: [&str; 20] = [
9 "log_whisperer",
10 "commit_conductor",
11 "cache_cowboy",
12 "compile_captain",
13 "latency_llama",
14 "yaml_yoda",
15 "regex_rider",
16 "semver_sage",
17 "kernel_keith",
18 "pixel_pilgrim",
19 "kubectl_kev",
20 "pipeline_pat",
21 "telemetry_tina",
22 "merge_maria",
23 "parser_pete",
24 "debug_duchess",
25 "nullable_nate",
26 "grep_greg",
27 "stderr_stan",
28 "segfault_sue",
29];
30
31static FAKE_DOMAIN_NAMES: [&str; 8] = [
32 "acme",
33 "contoso",
34 "widgets",
35 "example",
36 "placeholder",
37 "sample",
38 "foobar",
39 "testbench",
40];
41
42static FAKE_DOMAIN_TLDS: [&str; 8] = ["com", "net", "org", "io", "dev", "co", "app", "biz"];
43
44static APPLICATION_NAMES: [&str; 10] = [
45 "auth", "data", "deploy", "etl", "scraper", "cron", "ingress", "egress", "alerter", "fwd",
46];
47
48static ERROR_LEVELS: [&str; 9] = [
49 "alert", "crit", "debug", "emerg", "error", "info", "notice", "trace1-8", "warn",
50];
51
52static HTTP_CODES: [usize; 15] = [
53 200, 300, 301, 302, 304, 307, 400, 401, 403, 404, 410, 500, 501, 503, 550,
54];
55
56static HTTP_VERSIONS: [&str; 3] = ["HTTP/1.0", "HTTP/1.1", "HTTP/2.0"];
57static HTTP_METHODS: [&str; 7] = ["DELETE", "GET", "HEAD", "OPTION", "PATCH", "POST", "PUT"];
58
59static HTTP_ENDPOINTS: [&str; 9] = [
60 "/wp-admin",
61 "/controller/setup",
62 "/user/booperbot124",
63 "/apps/deploy",
64 "/observability/metrics/production",
65 "/secret-info/open-sesame",
66 "/booper/bopper/mooper/mopper",
67 "/do-not-access/needs-work",
68 "/this/endpoint/prints/money",
69];
70
71static ERROR_MESSAGES: [&str; 9] = [
72 "There's a breach in the warp core, captain",
73 "Great Scott! We're never gonna reach 88 mph with the flux capacitor in its current state!",
74 "You're not gonna believe what just happened",
75 "#hugops to everyone who has to deal with this",
76 "Take a breath, let it go, walk away",
77 "A bug was encountered but not in Vector, which doesn't have bugs",
78 "We're gonna need a bigger boat",
79 "Maybe we just shouldn't use computers",
80 "Pretty pretty pretty good",
81];
82
83const APACHE_COMMON_TIME_FORMAT: &str = "%d/%b/%Y:%T %z";
84const APACHE_ERROR_TIME_FORMAT: &str = "%a %b %d %T %Y";
85const SYSLOG_3164_FORMAT: &str = "%b %d %T";
86const JSON_TIME_FORMAT: &str = "%d/%b/%Y:%T";
87
88pub fn apache_common_log_line() -> String {
89 format!(
92 "{} - {} [{}] \"{} {} {}\" {} {}",
93 ipv4_address(),
94 username(),
95 timestamp_apache_common(),
96 http_method(),
97 http_endpoint(),
98 http_version(),
99 http_code(),
100 byte_size(),
101 )
102}
103
104pub fn apache_error_log_line() -> String {
105 format!(
108 "[{}] [{}:{}] [pid {}:tid] [client {}:{}] {}",
109 timestamp_apache_error(),
110 username(),
111 error_level(),
112 pid(),
113 ipv4_address(),
114 port(),
115 error_message(),
116 )
117}
118
119pub fn syslog_3164_log_line() -> String {
120 format!(
121 "<{}>{} {} {}[{}]: {}",
122 priority(),
123 timestamp_syslog_3164(),
124 domain(),
125 application(),
126 pid(),
127 error_message()
128 )
129}
130
131pub fn syslog_5424_log_line() -> String {
132 format!(
135 "<{}>{} {} {} {} {} ID{} - {}",
136 priority(),
137 syslog_version(),
138 timestamp_syslog_5424(),
139 domain(),
140 username(),
141 random_in_range(100, 9999),
142 random_in_range(1, 999),
143 error_message(),
144 )
145}
146
147pub fn json_log_line() -> String {
148 format!(
154 "{{\"host\":\"{}\",\"user-identifier\":\"{}\",\"datetime\":\"{}\",\"method\":\"{}\",\"request\":\"{}\",\"protocol\":\"{}\",\"status\":\"{}\",\"bytes\":{},\"referer\":\"{}\"}}",
155 ipv4_address(),
156 username(),
157 timestamp_json(),
158 http_method(),
159 http_endpoint(),
160 http_version(),
161 http_code(),
162 random_in_range(1000, 50000),
163 referer(),
164 )
165}
166
167fn timestamp_apache_common() -> DelayedFormat<StrftimeItems<'static>> {
169 Local::now().format(APACHE_COMMON_TIME_FORMAT)
170}
171
172fn timestamp_apache_error() -> DelayedFormat<StrftimeItems<'static>> {
173 Local::now().format(APACHE_ERROR_TIME_FORMAT)
174}
175
176fn timestamp_syslog_3164() -> DelayedFormat<StrftimeItems<'static>> {
177 Local::now().format(SYSLOG_3164_FORMAT)
178}
179
180fn timestamp_syslog_5424() -> String {
181 Local::now().to_rfc3339_opts(SecondsFormat::Millis, true)
182}
183
184fn timestamp_json() -> DelayedFormat<StrftimeItems<'static>> {
185 Local::now().format(JSON_TIME_FORMAT)
186}
187
188fn application() -> &'static str {
190 random_from_array(&APPLICATION_NAMES)
191}
192
193fn domain() -> String {
194 format!(
195 "{}.{}",
196 random_from_array(&FAKE_DOMAIN_NAMES),
197 random_from_array(&FAKE_DOMAIN_TLDS),
198 )
199}
200
201fn error_level() -> &'static str {
202 random_from_array(&ERROR_LEVELS)
203}
204
205fn error_message() -> &'static str {
206 random_from_array(&ERROR_MESSAGES)
207}
208
209fn http_code() -> usize {
210 random_from_array_copied(&HTTP_CODES)
211}
212
213fn byte_size() -> usize {
214 random_in_range(50, 50000)
215}
216
217fn http_endpoint() -> &'static str {
218 random_from_array(&HTTP_ENDPOINTS)
219}
220
221fn http_method() -> &'static str {
222 random_from_array(&HTTP_METHODS)
223}
224
225fn http_version() -> &'static str {
226 random_from_array(&HTTP_VERSIONS)
227}
228
229fn ipv4_address() -> String {
230 let mut r = rng();
231 format!(
232 "{}.{}.{}.{}",
233 r.random_range(1..255),
234 r.random_range(1..255),
235 r.random_range(1..255),
236 r.random_range(1..255),
237 )
238}
239
240fn pid() -> usize {
241 random_in_range(1, 9999)
242}
243
244fn port() -> usize {
245 random_in_range(1024, 65535)
246}
247
248fn priority() -> usize {
249 random_in_range(0, 191)
250}
251
252fn referer() -> String {
253 format!("https://{}{}", domain(), http_endpoint())
254}
255
256fn username() -> String {
257 (*random_from_array(&FAKE_USERNAMES)).to_string()
258}
259
260fn syslog_version() -> usize {
261 random_in_range(1, 3)
262}
263
264fn random_in_range(min: usize, max: usize) -> usize {
266 rng().random_range(min..max)
267}
268
269fn random_from_array<T: ?Sized>(v: &'static [&'static T]) -> &'static T {
270 v[rng().random_range(0..v.len())]
271}
272
273fn random_from_array_copied<T: Copy>(v: &[T]) -> T {
274 v[rng().random_range(0..v.len())]
275}