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 169
use std::collections::HashMap; use hostname; use log; use log::set_boxed_logger; use backends::Backend; use errors::{Error, Result}; use message::{Message, WireMessage}; /// Logger for sending log-messages /// /// A `Logger` instance can be either used as a standalone object to log directly /// to a log-server or it can be installed as a `log`-crate log-handler (with `Logger::install`). /// /// By default all encountered errors will be silently ignored. If you want the logger /// to panic when an error occurs, you can change the behaviour with `Logger::enable_panic_on_error`. pub struct Logger { hostname: String, backend: Box<Backend>, default_metadata: HashMap<String, String>, panic_on_error: bool, } impl Logger { /// Construct a new `Logger` instance /// /// The backend needs to be boxed for usage as a logger with the `log`-crate. /// This constructor tries to determine the local hostname (required by GELF) /// with the help of the `hostname`-crate. If you want to set a custom hostname /// check out the `Logger::new_with_hostname` constructor. pub fn new(backend: Box<Backend>) -> Result<Self> { hostname::get_hostname() .map(|hostname| Logger::new_with_hostname(backend, &hostname)) .ok_or( format_err!("Failed to determine local hostname") .context(Error::LoggerCreateFailed) .into(), ) } /// Construct a new `Logger` instance with predetermined hostname /// /// The backend needs to be boxed for usage as a logger with the `log`-crate. It /// uses the passed hostname for the GELF `host` field pub fn new_with_hostname(backend: Box<Backend>, hostname: &str) -> Logger { Logger { hostname: String::from(hostname), backend: backend, default_metadata: HashMap::new(), panic_on_error: false, } } /// Install a logger instance as a `log`-Logger /// /// This method wraps `log::set_logger` as a convenience function as required /// by the `log`-crate. `log_level` defines the maximum log-level the logger /// should log. /// /// Note that installing the logger consumes it. To uninstall you need to call /// `log::shutdown_logger` which returns the boxed, original `Logger` instance. pub fn install<T: Into<log::LevelFilter>>(self, log_level: T) -> Result<()> { set_boxed_logger(Box::new(self))?; log::set_max_level(log_level.into()); Ok(()) } /// Log a message via the logger's transport to a GELF server. /// /// The logger will automatically add `default_metadata` fields to the message /// if missing in the passed `Message`. pub fn log_message(&self, msg: Message) { let result = self.backend.log_message(WireMessage::new(msg, &self)); if result.is_err() && self.panic_on_error { panic!(result.unwrap_err()); } } /// Return the hostname used for GELF's `host`-field pub fn hostname(&self) -> &String { &self.hostname } /// Set the hostname used for GELF's `host`-field pub fn set_hostname<S: Into<String>>(&mut self, hostname: S) -> &mut Self { self.hostname = hostname.into(); self } /// Return all default metadata pub fn default_metadata(&self) -> &HashMap<String, String> { &self.default_metadata } /// Set a default metadata field /// /// Every logged `Message` is checked for every default_metadata field. /// If it contains an entry with the key, the default is ignored. But if /// there is no additional information present, the default is added to the message. /// /// This can be used for example to add a `facility` to every message: /// /// ``` /// # use gelf::{Logger, NullBackend, Message}; /// # let backend = NullBackend::new(); /// # let mut logger = Logger::new(Box::new(backend)).unwrap(); /// logger.set_default_metadata(String::from("facility"), String::from("my_awesome_rust_service")); /// /// logger.log_message(Message::new(String::from("This is important information"))); /// // -> The message will contain an additional field "_facility" with the value "my_awesome_rust_service" /// ``` pub fn set_default_metadata<S, T>( &mut self, key: S, value: T ) -> &mut Self where S: Into<String>, T: Into<String> { self.default_metadata.insert(key.into(), value.into()); self } /// Return a flag whether the logger panics when it encounters an error pub fn panic_on_error(&self) -> bool { self.panic_on_error } /// Force the logger to panic when it encounters an error pub fn enable_panic_on_error(&mut self) -> &mut Self { self.panic_on_error = true; self } /// Force the logger to ignore an encountered error silently pub fn disable_panic_on_error(&mut self) -> &mut Self { self.panic_on_error = false; self } } impl log::Log for Logger { /// Determines if a log message with the specified metadata would be logged. /// /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.enabled) /// for more details fn enabled(&self, _: &log::Metadata) -> bool { // The logger does not dicard any log-level by itself, therefore it is // always enabled true } /// Logs the `LogRecord`. /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.log) /// for more details fn log(&self, record: &log::Record) { if !self.enabled(record.metadata()) { () } self.log_message(From::from(record)) } fn flush(&self) {} }