{"record":{"id":"3d93b2d06a89ffb1","repo":"stamparm/maltrail","slug":"unable-to-open-event-log-e","errorCode":null,"errorMessage":"unable to open event log '{}' ({e})","messagePattern":"unable to open event log '(.+?)' \\((.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sensor/src/output.rs","lineNumber":345,"sourceCode":"            // ONE atomic open. The previous exists()-then-File::create() sequence was a race\n            // between workers: each has its own sink, so two could both find the file missing at a\n            // day boundary and the second `File::create` would TRUNCATE events the first had\n            // already written. `create(true).append(true)` with the mode set in the same call\n            // cannot truncate, and gives Python's 0644 on creation without a second syscall.\n            // .mode() is a Unix extension; on Windows the file inherits the directory's ACL and\n            // there is no mode to request.\n            #[cfg(unix)]\n            let opened = OpenOptions::new().append(true).create(true).mode(0o644).open(&path);\n            #[cfg(not(unix))]\n            let opened = OpenOptions::new().append(true).create(true).open(&path);\n            match opened {\n                Ok(f) => {\n                    self.log_file = Some(f);\n                    self.log_path = Some(path);\n                }\n                Err(e) => {\n                    self.log_write_errors += 1;\n                    log_error(&format!(\"unable to open event log '{}' ({e})\", path.display()), true);\n                    return;\n                }\n            }\n        }\n        if let Some(file) = self.log_file.as_mut() {\n            // ONE write(2) per event, deliberately — not `write_all`.\n            //\n            // The guarantee that matters with several workers appending to one file is that each\n            // event line lands whole: O_APPEND makes the kernel pick the append offset and perform\n            // the copy atomically *per system call*, so one call per line means workers interleave\n            // whole records. `write_all` loops on a short write, which would split a line across\n            // two calls and let another worker's line land in the middle of it.\n            //\n            // (An earlier comment here justified this with PIPE_BUF. That was wrong: PIPE_BUF\n            // bounds atomic writes to PIPES, not regular files. The property being relied on is\n            // O_APPEND's atomic offset-plus-write, which has no such size bound in practice but is\n            // also not unlimited — hence treating a short write as an error rather than looping.)\n            match file.write(line.as_bytes()) {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/output.rs#L327-L363","documentation":"The event log file could not be opened for writing. write_event_log attempts to (re)open the configured event log path; if File::open/create fails, the error is logged, log_write_errors is incremented, and the event is dropped rather than crashing the sensor.","triggerScenarios":"write_line calls write_event_log while self.log_file is None, and opening the configured log path returns Err (path does not exist, no write permission, too many open fds, read-only filesystem).","commonSituations":"Event-log directory missing after deployment; sensor running as a user without write access to the log path; disk full or fd limit exhausted; log path misconfigured (typo, relative path wrong working directory).","solutions":["Read the wrapped {e} in the log line to see the exact OS error for the path shown ('{path}').","Create the log directory and fix permissions (chown/chmod) so the sensor's user can write the file.","Fix the event log path in sensor configuration and ensure the directory exists before startup.","Check ulimit -n and disk space if the error appears only after long runs."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"use std::fs;\n// before starting the sensor, verify the event log location is writable\nlet dir = std::path::Path::new(log_dir);\nif !dir.is_dir() { fs::create_dir_all(dir)?; }\nlet probe = dir.join(\".write_probe\");\nfs::write(&probe, b\"\")?;\nlet _ = fs::remove_file(&probe);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create and chmod the event log directory in deployment automation before the sensor starts","Run the sensor under a user that owns the log directory","Check fd limits (ulimit -n) for long-running sensors","Use absolute log paths in configuration"],"tags":["file-io","logging","filesystem"],"backgroundTag":"file-open-failed","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}