fern-0.5.8/CHANGELOG.md010064400017500000144000000317461344633224500125250ustar0000000000000000Unreleased ========== 0.5.8 (2019-03-25) ================== - Change `syslog` feature to no longer re-export anything on Windows. Previously, using `syslog` on windows would simply fail to compile. (thanks [@17dec]!) - Fix `log_enabled!` macro only checking dispatch at surface and not at sub-levels. Actually logging still only does a shallow check, but now `log_enabled!()` should be actually accurate. 0.5.7 (2018-11-11) ================== - Fix colored log level display to honor formatting flags such as "{:>5}" (thanks [@ExpHP]!) 0.5.6 (2018-06-19) ================== - Add another fuller example for colored logging (thanks [@digitalatigid]!) - Add support for syslog version 4.0.0 under feature flag `syslog-4`. - Does not remove syslog-3 support - Includes support for RFC5424 formatting, but requires manually transforming the log record into the key/value pairs syslog expects. - Add shorthand for calling an arbitrary function as a logging backend 0.5.5 (2018-03-25) ================== - Add a log handler for logging into an arbitrary `Write` object. (thanks [@vorner]!) 0.5.4 (2018-02-17) ================== - Add a log handler which panics on all messages. This can be used in test configurations to turn warning or error messages into hard errors. - meta: add test coverage reporting via tarpaulin and coveralls 0.5.3 (2018-02-04) ================== - Add support for `Display::fmt` implementations which call the global logger via a 'meta-logging-in-format' flag. (thanks [@jakunar]!) - This is disabled by default, see 'meta' module for more info. 0.5.2 (2018-01-02) ================== - Re-add compatibility for rust versions 1.16.0+, and add CI testing with rustc 1.16.0 to ensure this is kept in the future. - Add some more general documentation updates and clarity increases. - Add a CHANGELOG.md which mirrors git tag releases. - Update documentation links to point to docs.rs rather than custom hosted documentation. - Fix ColoredLevelConfig::default being an inherent method rather than an implementation of the Default trait. - Add direct support for the syslog crate under the "syslog-3" feature flag. - Add a module worth of documentation for using fern with syslog. 0.5.1 (2017-12-26) ================== - Re-add support for colored log levels with the 'colored' feature - Support for this was accidentally dropped in 0.5.0. - Fix the ability to run tests on windows, and refactor integration tests for developer clarity - Update documentation for clarity Short list of changes in 0.5.0: - Updated from log 0.3 to log 0.4. Both are interoperable, but using log 0.4 provides a much cleaner log interface for fern to use internally - Removed fern::FernLog. - Renamed fern::color::ColoredLogLevelConfig to ColoredLevelConfig - Greatly simplified testing 0.5.0 (2017-12-25) ================== - Update from log 0.3 to log 0.4. Both log versions are interoperable, but line numbers from libraries using 0.4 won't show up in binaries with recievers using log 0.4. - To clarify: both fern 0.4 and 0.5 will work perfectly with all libraries, but line numbers will always be 0 if you use fern 0.4 and log 0.4. - Remove fern::FernLog. With log 0.4, log records can be constructed directly, and fern loggers can now recieve just a record rather than a record and the formatted display string. - Notable changes in the log crate: log::LogLevel is renamed to log::Level, and log::LogLevelFilter is renamed to log::LevelFilter. - fern::color::ColoredLogLevelConfig has been renamed to fern::color::ColoredLevelConfig to match log crate renamings. - fern tests have been greatly simplified with the new support for creating log records manually. it's now possible to just run "cargo test" and test all of fern's functionality. 0.4.4 (2017-12-22) ================== - Add support for coloring log levels in Unix terminals using the 'colored' crate (thanks [@nihiluis]!) - This is enabled via the 'colored' feature, and adds a fern::color module. 0.4.3 (2017-09-20) ================== - Add support for sending to an std::sync::mpsc::Sender as a log output (thanks [@gingerDevilish]!) 0.4.2 (2017-08-20) ================== - Documentation hotfix after a premature release of version 0.4.1 0.4.1 (2017-08-20) ================== - Lots of documentation tweaks and reworking - Add CONTRIBUTING file and update README to invite new contributors - Improve example application to be more realistic - A few small internal improvements, mostly code cleanup here 0.4.0 (2017-05-09) ================== - Rework API surface to be builder-based for simpler configuration - Rename DispatchConfig to Dispatch, OutputConfig to Output and FernLogger to FernLog - Rework inner log structure for more efficiency - Different outputs are now stored in an `enum` rather than every sublogger being a Box with dynamic dispatch - Remove LogError; handle errors within individual loggers now - and only within loggers which actually need it - Remove unnecessary wrapping of streams with an Arc (now just uses Mutex for File streams) - Remove unnecessary wrapping of Stdout and Stderr streams with a Mutex, when they are already synchronized - Pass around just &fmt::Arguments + &log::LogRecord instead of passing each individual LogRecord part - Move opening of files and stdout/stderr from configuration "building" to configuring - Instead of taking OpenOptions, log configuration now just takes an already-opened std::io::File object - fern::InitError is now a convenience API, and is never returned from any fern APIs - Redo formatting to work without allocation - formatting closures now finish with a callback rather than returning a value - Update examples to use `chrono` instead of the `time` crate - This removes another extra allocation - chrono can format time directly to a writer, without allocating intermediate the result to a String - Add much more documentation: almost every builder method has a full example, and all features should be thoroughly explained - Add appveyor and travis-ci badges to README and Cargo.toml 0.3.5 (2015-05-06) ================== - Build changes to .travis.yml - Add html_root_url doc attribute - Add file_with_line_sep and file_with_options_and_line_sep configuration construction options to allow specifying a line separator other than the default '\n' 0.3.4 (2015-04-16) ================== - Update for rustc version e9080ec39 (1.0.0-beta.2) - Update to use no_test to ignore doc tests, rather than ignore - Remove all stability attributes on public types - Add rust version matrix for testing on travis, to test on beta as well as nightly builds 0.3.3 (2015-04-03) ================== - Update for rustc version 9854143cb (1.0.0-beta) - Derive Clone for all types deriving Copy - Update docs a bit for that switch to `time` crate - Switch to time crate instead of chrono for tests, as chrono hasn't updated for rustc 1.0.0-beta yet. - Instead of implementing a sudo-time crate as a workaround for https://github.com/rust-lang/cargo/issues/1474, just disable the doc test, and copy the code to a separate file in tests/ 0.3.2 (2015-04-03) ================== - Update to rustc version 2e3b0c051 - Add a workaround for https://github.com/rust-lang/cargo/issues/1474 in doc tests - Implement From for errors instead of FromError - Remove now unrequired feature gate - Implement error::Error for error types 0.3.1 (2015-03-26) ================== - Updates to rustc version 27901849e 0.3.0 (2015-03-25) ================== - Updates to rustc version 123a754cb - Updates to log version 0.3.0 - Reworks fern::OutputConfig to be a struct with functions to construct configurations, rather than an enum with variants for each configuration. - This is a breaking change, as all constructors on fern::OutputConfig have been renamed from UpperCase to lower_case. - This also now allows fern::OutputConfig to be constructed with anything which implements `AsRef`. - For example, `fern::OutputConfig::file("some-file.log")` works, without having to construct a Path or PathBuf manually. 0.2.1 (2015-03-19) ================== - Updates to rustc version 3e4be02b8 - Updates documentation 0.2.0 (2015-03-08) ================== This version reworks the public API in order to turn fern into a backend to the `log` crate. API Changes: - Remove the `local` module, as the `log` crate now handles storing a global logger. - fern::Logger *must* now be Sync + Send - BoxedLogger and ArcLogger typedefs are removed, due to writing `+ Sync + Send` no longer being required - Now everything just uses Box - Level is removed, in favor of using log::LogLevel and log::LogLevelFilter - LoggerConfig is renamed into DispatchConfig - Rename `Error` to `LogError` - Implement `fmt::Display` for `LogError` - A new `Formatter` type is added for formatting closures. It also now takes a &log::LogLocation parameters as well. - OutputConfig::Parent is renamed into OutputConfig::Child, this seems to make more sense, given that you can have any number of children - Logger::log() now takes (&str, &log::LogLevel, &log::LogLocation) instead of (&fern::Level, &str) - Add an `IntoLog` trait which DispatchConfig and OutputConfig implement (instead of having `into_logger()` on each of them. - Add an `into_log()` method to the IntoLog trait that turns a log configuration into a `log::Log` (as apposed to `fern::Logger`) - Rename `IntoLog.into_logger()` to `IntoLog.into_fern_logger()` in order to differentiate from the `into_log()` method. - Add a `fern::init_global_logger()` method which sets the global `log` crate logger from a log configuration - Add an `InitError` error which is used by `init_global_logger()` for either an IO error or `log::SetLoggerError` - Update everything to use the new io and path modules - Add a `FileOptions` option to `OutputConfig` which allows for specifying an `OpenOptions` for opening the log file with Additional Changes: - The docs have been rewritten to be up to date with all the above changes - The code snippets in the docs are now all tested! This is instead of having `no_test` and not having fully workable code examples. - There is a new `tests/lib.rs` file which contains tests for initializing fern and log filtering with different log levels. 0.1.12 (2015-02-21) =================== - Fixes compile warnings and errors for rustc version 522d09dfe (thanks [@gareins]!) - Adds static life bound - Switches to using old_path feature instead of path feature 0.1.11 (2015-02-13) =================== - Fixes small documentation error - Fixes compile errors in rustc version 3ef8ff1f8 0.1.10 (2015-02-03) =================== - Finishes updating to rustc version eaf4c5c78 - Last version compiled, but had many warnings - Move all #[experimental] features to #[unstable] - Add #![feature(io, core)] - Remove unrequired .iter() call 0.1.9 (2015-02-03) ================== - Updates to rustc version eaf4c5c78 - Changes all usages of std::io to std::old_io 0.1.8 (2015-01-27) ================== - Updates to rustc version 458a6a2f6 0.1.7 (2015-01-09) ================== - Update to latest rustc (44a287e6e) 0.1.6 (2015-01-07) ================== This update mainly just cleans stuff up and updates for the latest rustc (ea6f65c5f) - Update to using f.write_str(... instead of write!(f, "{}", ...) for simplicity - Update to use (closure)(...) instead of closure.call((...)) because directly calling works now - Remove #![feature()] attributes for unboxed_closures and old_orphan_check, as they are no longer required. 0.1.5 (2015-01-05) ================== - Updates for the latest rustc version, ad9e75938. - Fixes all lines which go past the 99 character line limit. 0.1.4 (2015-01-01) ================== This version is *not* backwards compatible. The change was necessary for the latest rust update however, so only a minor version increment was added. - Changes from using IoResult<()> to Result<(), fern::Error> for return types from logging operations. - Updates for latest rustc 0.1.3 (2014-12-27) ================== - Adds a new public module, local, which stores a thread-local logger. - Adds a new logger 'NullLogger', which does nothing with logged mesages. - Fixes WriterLogger to append to opened files instead of overwriting. - Adds a ton more documentation 0.1.2 (2014-12-24) ================== - Adds type aliases BoxedLogger and ArcLogger, which resolve to `Box` and `sync::Arc>` respectively. 0.1.1 (2014-12-22) ================== - Adds a workaround for a bug introduced a compiler update. 0.1.0 (2014-12-19) ================== First release, version 0.1.0. [@gareins]: https://github.com/gareins [@gingerDevilish]: https://github.com/gingerDevilish [@nihiluis]: https://github.com/nihiluis [@jakunar]: https://github.com/jakunar [@vorner]: https://github.com/vorner [@digitalatigid]: https://github.com/digitalatigid [@ExpHP]: https://github.com/ExpHP [@17dec]: https://github.com/17dec fern-0.5.8/CONTRIBUTING.md010064400017500000144000000070261331201441500131230ustar0000000000000000### Contributing #### Getting started (section in README) Contributions are welcome! The easiest way for you to contribute right now is to use `fern` in your application, and see where it's lacking. The current library should have a solid base, but not many log adapters or niche features. If you have a use case `fern` does not cover, filing an issue will be immensely useful to me, to anyone wanting to contribute to the project, and (hopefully) to you once the feature is implemented! If you've just filed an issue, or you want to approach one of our [existing ones](https://github.com/daboross/fern/issues), mentoring is available! Tag me with @daboross on an issue, or send me an email at daboross @ daboross.net, and I'll be available to help. As a note, all contributions are expected to follow [the Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). #### `fern` project structure Fern attempts to be an idiomatic rust library and to maintain a sane structure. All source code is located in `src/`, and tests are in `tests/`. The source is split into four modules: - `lib.rs` contains top-level traits, module documentation, and helper functions - `builders.rs` contains all the configuration code - `errors.rs` contains error handling for finishing configuration - and `log_impl.rs` contains the implementation for `log::Log` which is created to run for the actual logging. Hopefully these modules are fairly separated, and it's clear when you'll need to work on multiple sections. Adding a new log implementation, for instance, will need to touch `builders.rs` for configuration, and `log_impl.rs` for the implementation - both pieces of code will connect via `builders::Dispatch::into_dispatch`, but besides that, things should be fairly separate. #### Pull requests Pull requests are _the_ way to change code using git. If you aren't familiar with them in general, GitHub has some [excellent documentation](https://help.github.com/articles/about-pull-requests/). There aren't many hard guidelines in this repository on how specifically to format your request. Main points: - Please include a descriptive title for your pull request, and elaborate on what's changed in the description. - Feel free to open a PR before the feature is completely ready, and commit directly to the PR branch. - This is also great for review of PRs before merging - All commits will be squashed together on merge, so don't worry about force pushing yourself. - Please include at least a short description in each commit, and more of one in the "main" feature commit. Doesn't have to be much, but someone reading the history should easily tell what's different now from before. - If you have `rustfmt-nightly` installed, using it is recommended. I can also format the code after merging the code, but formatting it consistently will make reviewing nicer. ### Testing Building fern is as easy as is expected, `cargo build`. As of fern 0.5, testing can also easily be done with `cargo test`. To run and test the example programs, use: ```sh cargo run --example cmd-program # test less logging cargo run --example cmd-program -- --verbose # test more logging cargo run --example colored --features=colored # test colored log levels ``` Feel free to add tests and examples demonstrating new features as you see fit. Pull requests which solely add new/interesting example programs are also welcome. ### Mentoring With all that said, contributing to a library, especially if new to rust, can be daunting. Feel free to email me at daboross @ daboross.net with any questions! fern-0.5.8/Cargo.toml.orig010064400017500000144000000026521344633166100135760ustar0000000000000000[package] name = "fern" # Remember to update html_root_url in src/lib.rs with each version. version = "0.5.8" authors = ["David Ross "] description = "Simple, efficient logging" documentation = "https://docs.rs/fern/" repository = "https://github.com/daboross/fern" readme = "README.md" license = "MIT" keywords = ["log", "logging", "logger"] categories = ["development-tools::debugging"] include = ["Cargo.toml", "src/**/*", "tests/**/*", "examples/**/*", "LICENSE", "README.md", "CONTRIBUTING.md", "CHANGELOG.md"] [badges] travis-ci = { repository = "daboross/fern" } appveyor = { repository = "daboross/fern" } coveralls = { repository = "daboross/fern" } [dependencies] log = { version = "0.4", features = ["std"] } colored = { version = "1.5", optional = true } [target."cfg(not(windows))".dependencies] syslog3 = { version = "3", optional = true } syslog = { version = "4", optional = true } [features] syslog-3 = ["syslog3"] syslog-4 = ["syslog"] meta-logging-in-format = [] [dev-dependencies] tempdir = "0.3" chrono = "0.4" clap = "2.22" [[example]] name = "cmd-program" [[example]] name = "colored" required-features = ["colored"] [[example]] name = "pretty-colored" required-features = ["colored"] [[example]] name = "syslog" required-features = ["syslog-4"] [[example]] name = "syslog3" required-features = ["syslog-3"] [[example]] name = "meta-logging" [package.metadata.docs.rs] all-features = true fern-0.5.8/Cargo.toml0000644000000037130000000000000100410ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "fern" version = "0.5.8" authors = ["David Ross "] include = ["Cargo.toml", "src/**/*", "tests/**/*", "examples/**/*", "LICENSE", "README.md", "CONTRIBUTING.md", "CHANGELOG.md"] description = "Simple, efficient logging" documentation = "https://docs.rs/fern/" readme = "README.md" keywords = ["log", "logging", "logger"] categories = ["development-tools::debugging"] license = "MIT" repository = "https://github.com/daboross/fern" [package.metadata.docs.rs] all-features = true [[example]] name = "cmd-program" [[example]] name = "colored" required-features = ["colored"] [[example]] name = "pretty-colored" required-features = ["colored"] [[example]] name = "syslog" required-features = ["syslog-4"] [[example]] name = "syslog3" required-features = ["syslog-3"] [[example]] name = "meta-logging" [dependencies.colored] version = "1.5" optional = true [dependencies.log] version = "0.4" features = ["std"] [dev-dependencies.chrono] version = "0.4" [dev-dependencies.clap] version = "2.22" [dev-dependencies.tempdir] version = "0.3" [features] meta-logging-in-format = [] syslog-3 = ["syslog3"] syslog-4 = ["syslog"] [target."cfg(not(windows))".dependencies.syslog] version = "4" optional = true [target."cfg(not(windows))".dependencies.syslog3] version = "3" optional = true [badges.appveyor] repository = "daboross/fern" [badges.coveralls] repository = "daboross/fern" [badges.travis-ci] repository = "daboross/fern" fern-0.5.8/LICENSE010064400017500000144000000020431310354064100116740ustar0000000000000000Copyright (c) 2014-2017 David Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fern-0.5.8/README.md010064400017500000144000000062751331201440200121520ustar0000000000000000fern ==== [![Linux Build Status][travis-image]][travis-builds] [![Windows Build Status][appveyor-image]][appveyor-builds] [![Coverage Status][coveralls-badge]][coveralls-builds] [![crates.io version badge][cratesio-badge]][fern-crate] Simple, efficient logging for [Rust]. --- Logging configuration is recursively branched, like a fern: formatting, filters, and output can be applied recursively to match increasingly specific kinds of logging. Fern provides a builder-based configuration backing for rust's standard [log] crate. ```rust //! With fern, we can: // Configure logger at runtime fern::Dispatch::new() // Perform allocation-free log formatting .format(|out, message, record| { out.finish(format_args!( "{}[{}][{}] {}", chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), record.target(), record.level(), message )) }) // Add blanket level filter - .level(log::LevelFilter::Debug) // - and per-module overrides .level_for("hyper", log::LevelFilter::Info) // Output to stdout, files, and other Dispatch configurations .chain(std::io::stdout()) .chain(fern::log_file("output.log")?) // Apply globally .apply()?; // and log using log crate macros! info!("helllo, world!"); ``` Examples of all features at the [api docs][fern-docs]. See fern in use with this [example command line program][fern-example]. --- - [documentation][fern-docs] - [crates.io page][fern-crate] - [example program][fern-example] ### Project Status The fern project is primarily maintained by myself, @daboross on GitHub. It's a hobby project, but one I aim to keep at a high quality. ### Contributing As this is a hobby project, contributions are very welcome! The easiest way for you to contribute right now is to use fern in your application, and see where it's lacking. The current library has a solid base, but it lacks features, and I may not anticipate your use cases. If you have a use case fern does not cover, please file an issue. This is immensely useful to me, to anyone wanting to contribute to the project, and to you as well if the feature is implemented. If you're interested in helping fix an [existing issue](https://github.com/daboross/fern/issues), or an issue you just filed, help is appreciated. See [CONTRIBUTING](./CONTRIBUTING.md) for technical information on contributing. [Rust]: https://www.rust-lang.org/ [travis-image]: https://travis-ci.org/daboross/fern.svg?branch=master [travis-builds]: https://travis-ci.org/daboross/fern [appveyor-image]: https://ci.appveyor.com/api/projects/status/ofdv9657k88jbpel/branch/master?svg=true [appveyor-image]: https://ci.appveyor.com/api/projects/status/github/daboross/fern?branch=master&svg=true [appveyor-builds]: https://ci.appveyor.com/project/daboross/fern [cratesio-badge]: http://meritbadge.herokuapp.com/fern [coveralls-badge]: https://coveralls.io/repos/github/daboross/fern/badge.svg [coveralls-builds]: https://coveralls.io/github/daboross/fern [fern-docs]: https://docs.rs/fern/ [fern-crate]: https://crates.io/crates/fern [fern-example]: https://github.com/daboross/fern/tree/master/examples/cmd-program.rs [log]: https://github.com/rust-lang-nursery/log fern-0.5.8/examples/cmd-program.rs010064400017500000144000000070571344633302200153000ustar0000000000000000extern crate chrono; extern crate clap; extern crate fern; #[macro_use] extern crate log; use std::io; fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> { let mut base_config = fern::Dispatch::new(); base_config = match verbosity { 0 => { // Let's say we depend on something which whose "info" level messages are too // verbose to include in end-user output. If we don't need them, // let's not include them. base_config .level(log::LevelFilter::Info) .level_for("overly-verbose-target", log::LevelFilter::Warn) } 1 => base_config .level(log::LevelFilter::Debug) .level_for("overly-verbose-target", log::LevelFilter::Info), 2 => base_config.level(log::LevelFilter::Debug), _3_or_more => base_config.level(log::LevelFilter::Trace), }; // Separate file config so we can include year, month and day in file logs let file_config = fern::Dispatch::new() .format(|out, message, record| { out.finish(format_args!( "{}[{}][{}] {}", chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), record.target(), record.level(), message )) }) .chain(fern::log_file("program.log")?); let stdout_config = fern::Dispatch::new() .format(|out, message, record| { // special format for debug messages coming from our own crate. if record.level() > log::LevelFilter::Info && record.target() == "cmd_program" { out.finish(format_args!( "---\nDEBUG: {}: {}\n---", chrono::Local::now().format("%H:%M:%S"), message )) } else { out.finish(format_args!( "[{}][{}][{}] {}", chrono::Local::now().format("%H:%M"), record.target(), record.level(), message )) } }) .chain(io::stdout()); base_config.chain(file_config).chain(stdout_config).apply()?; Ok(()) } fn main() { let cmd_arguments = clap::App::new("cmd-program") .arg( clap::Arg::with_name("verbose") .short("v") .long("verbose") .multiple(true) .help("Increases logging verbosity each use for up to 3 times"), ) .get_matches(); let verbosity: u64 = cmd_arguments.occurrences_of("verbose"); setup_logging(verbosity).expect("failed to initialize logging."); info!("MyProgram v0.0.1 starting up!"); debug!("DEBUG output enabled."); trace!("TRACE output enabled."); // Emulate a library we're using which has tons of debugging on the 'info' // level. info!(target: "overly-verbose-target", "hey, another library here, we're starting."); for i in 0..5 { info!("executing section: {}", i); debug!("section {} 1/4 complete.", i); info!(target: "overly-verbose-target", "completed operation."); debug!("section {} 1/2 complete.", i); info!(target: "overly-verbose-target", "completed operation."); info!(target: "overly-verbose-target", "completed operation."); debug!("section {} 3/4 complete.", i); info!("section {} completed!", i); } warn!(target: "overly-verbose-target", "AHHH something's on fire."); info!("MyProgram operation completed, shutting down."); } fern-0.5.8/examples/colored.rs010064400017500000144000000013131322037124300145010ustar0000000000000000extern crate chrono; extern crate fern; #[macro_use] extern crate log; use fern::colors::{Color, ColoredLevelConfig}; fn main() { let colors = ColoredLevelConfig::new().debug(Color::Magenta); fern::Dispatch::new() .chain(std::io::stdout()) .format(move |out, message, record| { out.finish(format_args!( "[{}]{} {}", // This will color the log level only, not the whole line. Just a touch. colors.color(record.level()), chrono::Utc::now().format("[%Y-%m-%d %H:%M:%S]"), message )) }) .apply() .unwrap(); error!("hi"); debug!("sup"); warn!("oh"); } fern-0.5.8/examples/meta-logging.rs010066400017500000144000000017661330631727500154540ustar0000000000000000//! This is an example to test the "meta-logging-in-format" fern cargo features. //! //! The example will hang if the feature is disabled, and will produce cohesive //! logs if it's enabled. extern crate fern; #[macro_use] extern crate log; use std::fmt; fn main() { fern::Dispatch::new() .chain(std::io::stdout()) .chain(std::io::stderr()) .chain(fern::log_file("hello.txt").unwrap()) .format(move |out, message, record| { out.finish(format_args!("[{}] {}", record.level(), message)) }) .apply() .unwrap(); // in order to actually trigger the situation that deadlocks, we need a custom // Display implementation which performs logging: struct Thing<'a>(&'a str); impl<'a> fmt::Display for Thing<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { debug!("formatting Thing wrapping ({})", self.0); f.write_str(self.0) } } info!("I'm logging {}!", Thing("aha")); } fern-0.5.8/examples/pretty-colored-screenshot.png010064400017500000144000010241241326473443300203630ustar0000000000000000PNG  IHDR B j`sBITO IDATx^]`SYM6^{2(eo*Cq<8>dP(BR{Iof4ۤ>6oθ~;J f/^ SRWGGGGGGGGGG@Y7ߝ s8T+hA/K~ ?k.37  )̞1Ǝ^Úg !f'~"7ֹe)ߝ۬_(z&3l}z1޸p |fMg;JZ!F[&~1cɡ,[pdN}/&*kV\K挰K3r2C?dֽ_8p"-Pxri 5cAԫ\ks-Yҁw ~w?B"dY[nfE }_gQ!qSpTw޶n*ﯵX/XcZ[74CؽAXcS<~ 2``4 fPQʄIC-r:CCwZt5pϕ(|U3SؘwGUן<"iN5Ԑ|ea4Lj/r0&qj2:P/kXә҈t&;J`.m^޲b={=3k7Ph/{ MgZ+>qVfD0浍sǻ'EᏬh Duui'~ꝇ] &2C O= xq;F}?} YI( Ͻqs$؄}ri60`]|? !BY=e nަ䘟_JoQF B)0cj\`P p8֋}̉ =|~@84[=on<#_ xS+fAP $  ݩOdM].N5 ޸jrOډKoxR7UˠsVᧆ$ ,mT'թߎpg:R9s;q"CuFGC3g|tZu-7}Z~suwчƙn 'WEm궡_tN[ lq|Ljgz>-x?NB!äܞ| 5fd6l>l\lWv=^\|O/bRQlZ`QldޥWFyܚ >Zd h dKe/ζa']߁Nkc 5D홭X*7ޗ$4d"0%OY%8ކ%k3akXhP^ٷk־y. !BuJF`H}Eш$%_b%|G-A1 xь4^Hmv|l`@OhܕsoO2xbbxD?)Ks}t#M׶Mm8sb:"hU B L#hk'|a { h~g`В8ӒsХ 9P8q+ir㏞y(O۸1;ϯ4qmElX +)L~2bN|(Zl2JN~F^TtitYĥQΝPm/q˗`ih9\mtU;rƀ/Lx (.hc##DW?dGo߈9{hG_MMLĮbvS=k=y)IdwAvoOGmNݿe (St}V`HvS:"[w6ͮM!ۘ{VIehrZh+#e hOI%soD$RV8h|9n=Ί uYd1"˥kߝOd*h`ᯩ Hy?.hqJ引''C9cdp)*:/CswScs g׸1tn eP<ܝtWISn1kc9cσxl3Czp7|{oo8/'[q +bxb+бrts%+܄ٹEѪ!CmC*e^#]ϿZՅ}'@4."qyi|uB&XVU @@ )>H+\lVk]  'OO:9ܼBh"D4Q}l8,W+\.(L0 q৓]!-5W( \ mxYB@?XiOG>\/!ρ;NEoXhW|?2_\vb_Vz)n\ Re-޺qw>8юf4mF.c __QV&&89ed6h 1Ƃ$N/͆NUGҩ.hdd(a+VV>:sCSO;6ׯ缰qoEf!9$@mJ@<6vr0IJ#б"AlweErKFCM496BnKIOn xAS˿:`-Zqa҈2I>'q>Z Kӎ4ŚV_T ꌆ$,H"N54ۦ|6/ "jPsI6N=..Twu۪ש&ʎ9ajVk})XA`wu!<ŗ| x&7`1~c:Vv9,g``^So$,Ӝ+ :)߽ݴM?4gf$!ĞDǸY/m۰sO[nYIpOѵEwds3CC Ch풁@ouT裙>"p(Ir ]0-ҝsod[s #jizh)pԷ3OwGb>~<x)yb [Q4,ʦ&bgjb6@r v%9JbFrYB@-^ zy+_%N..*z/.hB Rcqy?iAp[<:h[w}Y/Tqz.P2 [nFZnGMzU>~ G,% b }Xa&Z(XU LuCЂMd1'>5ZeimjA R#8YRxF%!(r\{D=-NsA%I+\yŀ sۊB/I>$?!6@61U6.jz HLV}l8W+D#Y*qO=!2a8m؋ >5/LsF93\}򦆂Iko]Nzxge&hxioi &V'A:+@(hfWE;~^t_9o'sf:*38c#Rby[_d00]R3 X|s\ι6F4o1m/م 0jjpB͵.v|7\;|yw0hNcX,c |tr[:Me~ izr g/r3zAO0V]Ĵz-#̀0uݷT@1''&|{Aid.=/}xNK+lLVкH/k[R)AnX5yr%B_ͨI,&t2҅)h#Y rj d)vכݗ*Ґaict'3@xmnUr4kU[łRSGڦ4?zb=c"w#U];&Qhhx:?85c$4Tڮ_>>>盷?ݹzuY{I'w>0HpG$6\tMQo]oFnʋٳ셌n:\t%@@d*uw'-+<ʼ?s0otFIlEx b7~sƉ)hʍdOG*/ֶaC#4%tr}y1LP+/ nǷ-XXsV͚ L87,"o?ȋ y6l9zL(?-W^;/|}eIc1"(Ɗ~;fRGɃm߀e'.& ;[*ח3ծT`ʇ{s%JNЄ=]џ[r1(XM!­GG>}V`2})+` 2~|+[?>Zvmf 1W8?` nOg2d:<)4t`Fc /#########0+/=pD1n!T+hA/K* ?k.37K2{yc?zvQT5wϕoȾ}1 *&9tȋg}r+Ψheрo\qUd ~^*i\3-Ey {gI/gz|Vߖiu@>'6]~S ߾Y/m˗;]x-+;H-3dwrxJ_"ؾa"HVZֲ$RhɦfMt~R&WH8_L7WD=Z}auV"_?;N888Pwp̝_vQϭXG+}J;waNJ%w]2ed KR{ ƿvpR.nߤ[lߖ~b~ʋܒrRqFCC@4 ͢O;_pϬIXoA+4H-iPX|82pd5+p.%w^zsFXČ9Zrֶ~I]x3AĿJE8]R)Q!z-x:}lAi{cbtk9#YKS 0'SuTD`#L=m vUO_k^2R1Ǵni{;:x&j>з>=z53Lfѝp^/>}6ӧKBPX/x{=ɻnorFA6n/{[ n;ׅQ YB+4ZMwz,pR1M 23Ł-`g W:].yѩF88>C{%njnNC-U^!5f|8X/Jb>RcS<~  `hh`92!|-P:Xh \Hzs鏿W:3yw~>x$Z%y+XC ɗ}q*#Esʈ c&# %Y)Ml/ -Ҧ~|a-k*C߳:vՊ$ _ذh=SZ{5#5m;\8)duw [4Z-eR]nݹnD=ÚYh7uI n!TJWb>JAy#l\4͒TK-XMf\=><#_QY/D#`@r?y5<'EeW%;pY/i)N3tsbkqoh;_@ V~}q{|8R卟ciKgsOGKNq\ke^6k~j#eh8c/###`c@HPUY/K#! }R/꛺v]j`q䞴05 ߶% !^Y454TUNS]sׯt4wE댎fmil1> IDATY?v}ỿCRHp[ktͫ6u/@:G'-]S{^ZhS>c_>- Vy?N"BwXls{;gEdK 5s<[.~qo6>Wn_qqʥwT/V;)FVu̖L]zeڝg8ϭ*3LT}l ȅKJ:)>H`=70|{鏯Z郣~ c-.%_b%|G-Ahj#4PnI^M&)۰d{&l{RJ+vM\ݳz7Oqօ:Ѐ 2/5KߙK{yDu< %p I^3Ha<GGG`8T  .ޓO  H rI<O 1T 'ebN}@x`rD5ڶ gP,`PGV Ah` izO6,[ao9b0yt 5ZvqZrNTa`U} 'n{%\nsq3%Xi7u|՜&._-Z?d%)o5XF RO^mB&Qɱucɏȫcwsʒ.?.+@4ʹ֡*;{8c,}#X^5Ǵ-{G BQgWJZ]h}}DЫH"\PP-h86Qw|oX<>!ۘ{VIը5V ׏垼$ e\=ߢMlvmⱣwCn 9;[]f%[m g be8+5gug-TIpp`.H1k/SFR;@" d/sϼbv]/(,}wbp> Si62gj4T@$@伟D8 ~r` ͜jg8 sXˡ٩͹C]׸1tn eP<ܝ (?&(bxrL1oB=tq^NAVNŠ#VڡcJnW s03|E8Lf2čмuW72)ZY[U! &xX -E0 q৓]/} ҧ}B:Bс$ ֶNXVS-'N^/7ptA8zzzT<# gdz g%O. I?888ÍwiOݸG>\/Q>~=qTjv#.Mi'6l}aeEϫ_n!cUwv:e2P.k{vL4n3r .lE21)#uF1$huOp7NplDmS]P~w΀q9ݧ s^8s7w"g{uSdvu} `K9j,=0#б"AlweErK;t-Ba16ʩHAbj=DTu~?ce?[6=Kt_wW\_:{4JPQKS5$oXH*LL>3ڣo#(b8k!C/C¹_ؘ́^Zh27 {g+u'ނ#`T-lCjdZ[@y(=W-AT+˞1ρA|ѥG51e&.&|O:㸙_Y}@g9?$44i5vƶ3b D{:nټ$C'8Pm~Q ؘWv cT"]nKQ?%XfLX_!Gg0) IIvXaJy  LMe] Q"^5syB! 4 v24EdOIhcGL7| S7o}0"ɔ&|C)f4"m=[/t"d" woFM`1T㤕.ܴ܁1[- 1ۡg.ɎzWZ2lC7m 3e A,KJOfF :79>/`Y+$ueJ0ng"2888CBdX:ɄM~*\۵ҟ|;].+v?佷n|z:n+5{} >.|~S[GכQbl;84rd(@h@I{uˊ/Oƀ2o Qt-hn!^©Xߜqb r?SW&}vnk۰!jzo~Jzi>ҘN&(ϕsi,,&=z2x Kw)nH1B_/ [3m.J:gBk/2Bο;0Ѫ+# Ɗ~;fRGɃm߀e'.0Y/*>SëK<˗.|Fɍݡ94Zi9R+ВYcXMGG>~DBM9yگOAd˿^Ugh}=@jW]ߵg>GY_Z ֢k3KqVP/:,59-wCձ02u888ÍSrê7Sܕ3Bp~bLo mGju+rv.ܟ2 q|Ms˖Qh0ECgy̞ &[Zߛ>gr w%\}kSnhA w.o4:"5M1x8)_L:3i5ӼlTז7܈ILk{5>N|G?<վECG)@W߆aWVEAl59gW3pxS'?.@ ;NCC'x2NSyÉqppppppppN{Rrqppppppppp0#O1CM\&;O]5Fz$K7/kS#:Q_Qܬ.Eb:u27 8MY3t3;'W+#jlހK~HL{`o &$l̫`=@(W=(p,cX 8`4` 'GK#2+5! U 9fZ4XW9Io4ǃA7 :[oEI8I!M[𵳲G5YQ?M,-;*]$q-5EUo /s^NiToWS@w; &j*HϭzE. RM0-|Ih!`Z$U@t^:t2(Jd`L5&g7l Ew.y/:AbX>Ѧ5%nIB#Fy.!$Ć[^M؄̘l}p#j:%|^\L1X9֋.胆rK:3@! 9S#U=wO1qpfդWbzсtI4HXAuϜ}>}4[dه.ȹ|@9)U  ιB{FDoժ~k.LŚb}I"0焊>34t\slc,Y@@3[T$[Nt%Oeƍ4)U~)ch0+ HF^C *o_M5~Q q8OPO˙ͽX#n({A [Mƌq)%\UZmD2pq5礖uzK) cK\EtaMvv#G'4f׈ ]f!B+#2pW[=73 ͺ*ShPB,:RUw E}ݭIGt@CQH-BA@Esu-ٿ@-qrԸG$IsyI;_ȫ/Y0Q^GCI#ҙFdQ[(c-YDJ au yn@7Ї bcmF̭WmldmEgH%4 41G68*9AE,1\:Di Ŗ`0w{GFe'Ve&gTDnAFz6%ܨlaqeKJdC`%Fw5VfeW$n`gkU\]ؒIEWڕ%l$zy]3s tcY0ΆJ8-o56XV_^ԃ)j,d<ތNͩJOvdztjS6 n,FVao5dT`ǯP +Htqe)}M59UV va&9Ԫ0g.g?ϵ t2$SR[ D# ~pWgՐ2Pi hDjkb\_7skːCځkTL)bxJfB姛y~/$AGN+9DӘGSZ^کU,s7 xmeYō/`4V<e@ +m]jejX/+* nOi/Wboܑa:uJGl^T֦ %viWwd/]6joT^ 1$j%2r  v"PdkYƧz  Qr!i :gNjh`ĘīO t67$ 9i]AYJ$y2۲F댎f贚4S}L Lqe X!N`6?<-͵"p>3ؠ!^LUNy=E{p|;0WXWRd,SjlMlʨ6'[M&> kr%PM}#=I7{iց'DwjXRXA$m.wQ,}BIPm`'qN+Qg}FSƄ4^l+E`Dv,|Z#FiDЈY(}츈‡BوWуɕ _[ { syq_m^̱<5]i%ÊY|?>It?94\̹*o9(N~ 7vռ1I64WŢsFH"Y#I*Zk~r'%/kOp`"J%YY'00wAdFP g:#fk2PlTq [ےŊ== 'vSG-o'=ĝ ӟ[tp7Q%LRlq2ʆF㬢 4EѣR!sp0+'&Yar=umDDOizV-nM-0tW@2W&dִ^{eF><VGpzO[Irv#6 TIT_V;8[dJl.!̎I+J<ݸ"%WY^)4 jV .mɅ-|~WMf@mIZzs7-R@P{SnlLmYʐ> G]IV)Y4BpF8Nmϫ꦳E:$= z!y]IM<>]Q mGҤۗ.^:=4iFd=2v]U}f=t7UgG=E?rZɪ,#-'S }eh’]WQ {9͕ yn(b$έ #ErվZck9&\v~wS+`Gx Ag-}Fr1{6'2D$@,Cfٳ\*{܎hB͑qAK q*XHU #!j<253U>mN+qmXrMgtƆfh9UQ>ʄU)c,$kkțXQ6*hke=LrL+>ŭe^6u}BXq~K2'3k4~2(PKKܾVjX^UGCNJibBlU 5C`8 G|ŪRF3j?i.Il(%D,v|}:"Ь}\M idoEH:ׂCjuBf4x'̔]բ_qsk N|4 4F#;7o{Z{y M0 jB ` N'/gudS ]i%EXim%ڛ0cZtu\\V4 bdL8WF4lNױ8j_AãP2 Or_hkŠh>#ئD.Ƥ f|DP= G䑝 xwnn؂&KZИ\0/й0XK7 )\V)U%}p .8+YhFCJҧ]L0aBP7Ah D{zrب/>"^~zT=`T^ #äIֲqzɋ]̩= :pFzLȠH@оC#O!B48 nu}jةdBgV.uaJSX,-jHjέ #1Hr,վRkgpdZx8ZbƇrU[Nʝk82P_=tA U1oͼt6S9P.BNZnR?˜ 0qCBCX8l96py3HJbAVt젔%K&’ꏘ+8%q3Ջi[QV8N`«|, g NWsLɇEO۱}c:V^xZl9!VDk{a5o8͒&XVD6m[k[kb$  m&æ6upp4MV8'#W 2zgW9^5Wz5 qd΄;aa⋰E!WB_Mep cSP`oįMU!#"u߇ "R8LlC[9 ?VsrN!~'4eXT3ERqȹbF%8DP>r59 e[; xL!8+NIfS&;lQ}7BAV6yl ގdx a|64dȽjۖ٬~!#\^,a @Um@~CScfuFF5 Y4t؊5ŒP!a05kF+u44/.H v"r{Kj8u?`UlmoK&- ێHfZ"/'K.BL lky@p$-A4Rn.q%Κ/w>^T مEMb,+nn.rE앞YKE=ְk6VuD&>*!٨ֶ̑>ssCYSW"An!]RgiJ(﫯Lqt`Qzk2]?}jd9U.a bawkM~2Kj@- g$du+i4T546V\5r}8"jGe4xCqT$Έ" Wst_z㻏%Î 1.z$xo3ni^٠eMxqʊf^, )d=K&r,5]jE% h (]Bqf4 nU vOW.T@L؈G58Poa̅2Vf0}T){l8C̰q'i)[ :,m:PL3mXD]TvL*fd?6ؑ^T0Sass݄2@gLzuy0ӧ1CX0 M"jWn+gIMbe.U@ܚg29ޘF"QDžȣFLEcꨳ݄J֣[O"t~EA9s%]O tV# 9[T3W'yS`x NP-,{fAG!c|g.OPlHTɛ]X-g̐ĹpKo|<񣑄]-b0{ru!g44PĂЀzƓ=/փ]MΒF`x@:c ׾"J)9 "nKXwݎ <+Nr;[kr(G&!~xw]C͂sEUU=nXe r=Bww5WɳEm9qw]OsoNl$8%- Wԛ_$DۯƀX5fN" [$=W>fW$aN.ɯswfPu %u- $Y?/O}/wz➮JՏ@ 6@|e ݕwE~l c7r}8?1pO^yQ+& Gi@qa3SdC / ^,>F?9\B,z6*>~8æª^OsnYBhgQߍ酲ˬFxՠ#G>^ţ長 Y8˝Y8ި?8O7dWU\U[Cu$g #t@EFQ)(BʏUy: [5Agdx2<"888888888aO_\#)To888888888O%lH,x ԈNtԗf7Koݖ?$C_'s>W>797nf`Št]p7i`+\Wx:P`8{1TW !3*h ,hiD#Vffv`=&s"Z!'Lfy ȇQjhu&Q=#_paՓBRYѷrjU;kBPgR阅3 F<.(3s6c"k0'JoY唁WidS@w; &j*HϭzE. 2Sï-|Ih!`Z$U@t^8Fqp3.k*LMn^?Z!Ȼ΅uZCD[f`9-I#N t47/Ii34lN"/w]9džR߮l[zM>y/:AbX>Ѧ5%nIB#-QTnyQR7eb2c=7^_zq1iWL! cz M"qFC,OWލN>!U^Eҹ_'m#b=>sn4[dه.ȹ|k4J`\z + gDTZomҥI2XWL6iTPGcZyNkNvX"hK5E Pm̠U[ɖ#DEkbmqc9 wJ_| *, ()#!&GNn? \Գ&(S4rfs/VGc0ޡ#nh>Vө1f\JI3W weuz:Q \|\hpGo.W=]X 85"GufPʈ \'VM FCT {iBQ_wkyy<PT3@PqPǜE]d 7Sfv,o2Q xz3VuvT=O,Jz H%N6hjf\^˪f̧y\g 'ՏtKgEmMhA`/"WP Tq!@$b3[^&xX*"po1kñȥ `V?ohsG.Ad9ȍig#YV\;4t,boZQ.ٔpbL!ĭ/JKk]ULf:z9Z2iD~WcefZv4=SB+X:%xQS+ذ;ECPB-]X@vc0ڕ{Kg!R;~}œ4XGD+܈Nh̩?\ *֪0g.g?ϵ t2$SR[ D# ~pWgՐ2PTQ[[G ^[ M^bzNKS2o*? $/!! RH=E?rZA 28ļNVGeLVk/*n`  qX @̕sI-Eڕ^MmnOi/Wboܑa:uJGl^T֦ %viWwd/]6joT^ 1$jG2r  v"PdkYƧz  Qr!i i(6 '54TibLՋl:H4̶!:DStZM Cϩ>S82ZL}'0zā YZD`MlАv/Cp 'ܼ"yI=8w!,su%NN25vĦ lxenPijN>&Q72S\t3f8yBdzl~,uhjD oۖ~ry7,ԝuxSe &4 魐ˎ/hCyհJ@#+n^ҝlAm̤THho8'ר߳AB>#)acB}eP%YFN|Wigj(|1qyd(M4ntIt?94\̹*o9(N~ 7vռ1I647pElD8WFT>Q1cOK6_מp(Eڗ룩KIwO ]a6a8VуjɌ4^ߡ {uFdب@%qK1+zzO Q,Z2VO(>z=,;=?lʅn»JTaɥ، e) YqV)> B4ԣR!sp0+'&Yar=um`zJӳj9|~wKnjQڞ2!'+3yd'>*m{Jk1I .ْ +d 5Vb0p dv=L*o|^G]QlYƽ)5J\P+Z`Euvo+L.lj2Ӱmj;L"sqOmnr-ҷ5ڛPwcc'djRh|lqAIV)Y4%Q}V^m\.$( $zcl\Hof|sd7&Sl5cA`z5ި i 9x{?;wyo~v2|5%&ggqUk6VEk,3(TP΅n3LS뮾36,&f޵ǣ*m/|3 \ Z+%?"*9[Z:tvr}aR$mͺRa9sNje"fE3$ 9ײL~w[RK8*Ϩvl?RRc!D!# 4zebQ1%~nN ָJy,@L3('5D##AB<*٤5Sʋe~&6YŠ3142S&FSLZ xJިѤWU|>>G}f+A@vm-ҪQaI~4AV^! 9U9> 9>6 &D*]<{~<٣2v -H;NQvz0LgޚzI<01P7׹a@_ QTEQ&I(]^/4x:"1+;d[RK̕'sۯ]Q3vz}F#&ȣJ>"VCgǢxa@o>ܦtv,ΗnsAйx2t\vCo=]=G'\&،$~|,!FD4cAo7qsAe,B}+mC3MzԶ"p;쬫RWvFBN'hMGe/!v`cEbrx;X&HaNy04Q3DօY7T(J걝g;x\pxmo!;3:(yF$] !?eޓ@e \,0ۍbիL#.ܑ >¡+ bGqƆ+-,kf$]J & G{$&XN[,LzpH@ k kK t(,QuSD 2BieCbJ ͯŗGA|fraqb%i!BH<0ƉQ>Q7Ri8::h~E,[ [ CՑȱ8K' VJx>+L<̨7txYpghr>=Z%Q*z;GNxQ2>cPƔ(<vm$%P(Q΄.2\G(av쁠 <lSFjC{Ogr_|Z 鮨*Zs 9:.7BaA=jlN4YN(v#Q4pن4 Vϧ#H`p+M(ϹK_Dʄ|~ SN0[aar6bd>߇.V{zssMuZ\bS>~>@ZϠ7*5e[WُTIiGx-aj p*w Mc'RfeHY(%{Wvi6xryM%ū|݌6TԌ~~U$4(xz徫A&a+zUx_K5✥JGT* b/ruMU̟l&sI `#J!]~v#qŢ }ξ5.Ъ}j%"PM~˕r6J/.oJ"Z.f% *8U@=I;z85uM|^ xs|AyĠLsZ1{tZ"U/m7 *)sTɄELx@kjomKkŖmLSi,nUC_[ECPmgp4ffH=o4CBY#3|*7<&~έF? lm)т:m%5S$֞ozGzťL Q*9,ftd;zZy *U84 "'-itZYt:G%t^>-pkL*__dzI~<]`Dzt/{>Gzg;]vbȺ5`'MexZ^cu ԧŲs >cPƒ(#Z` *XT*K8X֖l'2 ζ#o<퟇Vj[CX#U|f裿 `R5ہH4t4ΰ04g==ͷ>#^a.g*Qd=}/UEOJAױz Q4B']M#(~ͤ[[ nQ^>~ ( P@@ ( F&B ( P@@76mP Rw74*JXSUm (\IO_{ʯ-8[xY^RY_p-e+n W*r}mFbM$:7D{ihGe\4dG ̩j-/aNL]+mR <' h}$J{3 .x"F;'y~]P,]jݬ[=-i,|ۏї}NkqXȠrn)Vնόɢ/Gh{ͧd!{;en7 ~fŃ1$(] (Oa|3r@mY@6l.̭‡#඿YeuvJC[Rnxht ӪzV~}||t _yGLa 7֍LbQCf7^룫?$]RᢊЍslЀ##.hgTyyCi9n&osniV|wṲ"~BV4_Py|; rQ%1j8YVjXCpknrZqCNbKZL!;!ҡ:Jg6%^rX5)sl^PHaRUps{ )@hdм{8aTۣڏ<:FUM4S+egvĮ%1wtv_PQD蝾ϩ_Z? Kjφ2C3nSqP}s\w 8e8yBѠzօ/-01)g!ЈID8h "6gevN65%׈n[T-%WxízZճܸ# g\;Dp"2[gkN>udDDտH4syvh7Yb}K+!ItԊ- j}r[/׌\\R[evJb ѧ׮_wىRkF^-z%&M6U E7tDB#tbө@G|oRQEK𘵻BDoX)61E:j^j‡}x إU,Nc68˖x\&uK$h0TGdf^$(apURP5gl8tN 6k׭ϯ/(%u=C!uPIIVض=h6[je}/_&+s&+)1z46n>p|7!a[0EWQܺ2fm:X_ܵŏ:%hjs{m'* QH vog[ {Q>ia >_:~\T^y4Qv+*er 1g;ONneɮ\ާ6gԂ+= ~/óf-ygp K,es$J$;]NDW.U]h4{j~yӈ4!-7YS^(Aqy6XsC.)~`BiO=Ӵs˩!<㣑2)~L\@MQt1mv#y8}k؞a*d&8 )ֽx$`IM}`vT+c FlX'S˻oӀ.Ljl^*[P(Qec7C3MO򑡱Q҃ oBKoX!j{J;}tX=q'Y+qB/ >=˄ԈjXkQ%35r?kDGQESϵ6ǤG*5]E}x!r<Fs3Clޘe2PzmoM"r*E(")kۮ6e8ZbmnE7)6i{gfzKW͘/ז4kH` jdZ)q*nޞ|| 9YG_VN>iW)m_"@*X|k3s<#LƲױ@a,EG4Ѫ"ʛ Q+Á6Rɋ35OS`UYMqnrVtkw>;XM@NA/d۞&7Oo67L$bVSZ[ڋ.&urR44!@Ҟԃ`ZI 9{4."As..k^xuaʖ|@)&綨ցZ rwV4E $àvoŒHIfEJD"Q,mZמ(MNmExɋKOm_RY`ŐJ̛s~xA ($'խt>ģېjQ<?x~_Št]~4}d;u>DrɳsprKb + ׵X͙uuhK*(lAfݙwuWT3NZrQ|j6FFD\?ڏz-CƕW--:P;i)F_f]9?rxSQ2sKD XOМgXGH~w[RK8*Ϩvl?RRc!D!# 4zebQ1%~nN ָJy,@L3('5D##AB<*٤5Sʋe~&6YŠ3142S&FSLZ xJިѤWU|>>G}f+A@vm-ҪQaI~4AV^! 9U9> 9>6 &D*]<{~<٣2v -H;NQvz0LgޚzI<01P7׹a@_ QTEQ&I(]^/4x:"1+;d ׊7:IZL+e珫0JxJa[7v[[8._Q2)>cPƒ(26Ghَ縎 U»i?;lV׏a~/#Tj4Wpx oI-1WmvEF7-}ϏN^aLr\`Yx 7:ì>DUBoYf\S%(UvrW[ruD]݆LOnx^Hd$w-yOag v'Hsjۋ%W63M{+H>rG&`{Ks \W(K.wzD-CGa7((ŒIgvif*)_οزiD493eLk3q(QXq$i? kGS"HjpW9K4%D\ydp̹xjۑllvFb6Sv 3ls:I:1WޤEqA Vv߯&J}%um"Lv)2R=U®gh`1IIStzhQ#~\w yO}s=EJllvi#UtfԼ) ]3NOhN쑘`c:n0q"!Y/I(֯.U|48С|hDM*Ɨ q*+*4m2"R9Ʌ !8ZOd.ƉUpez" 'FWhFmHmg6V`l5l%UGk fH".jbU}iu&hv+*hvwAf9Ѩ㚗f9AGDRFBe3X6>#Rx(ͯ4pm_:Lf?R!('"QB>=綄3hE*$7H+l# a\dpO0&^^mA.xA5w3hPR3UHHҠRrUu}-ֈs2+QT$~5UI33Ź&+tq"G<;֎2Җ;׸|@>]hC61!$R&"0K !t>ǩ,f \X"$LpKK>le\ĭ"}\Bi *vM+ϘCM^Ilvvd[=vJa]K_΄so[\i)gӨ&O$2n&^ʠ᠁CZm]?Wu4YS>;$z﵀7gJ :M zJ] "ޘ_vclQɠR2Qkq<ڬ!NLXD6pV杽> H^l9Jچl:V5UD:5vF]O Ac{jiSLF9$5h)<+rcohyos: by+o!"-?+^^c1E"AiwW\ʤP⁛òh^XJG#ǠYC"rҲϟJ7yEsD]LGecvH˙Tg1YxF鉨 5^~ST}4v"#d};()c;c-`^o湸vw7 |=cӇkh9hF aah 'S{zo}CGv-3 \0 tϾ TɆ{^c2ch[O:FeQ.I&~yvz;F>hg]'kzT#K/4q?=hhuѓ@_Ww~E:ؠ;S3+77o M U@! v+vy){v՝N 2?i*Q5oш :گ߹B yUC 0D˷7YŰڥYrOO)>A0s=q\ ܖ]}iH0 ۉxKk Ik燆m?_xV:8gLU97/?e}Ϟ( 1Sc[#%Le:Z_Ik VO["TGZ|p;!ױJr?$>_:e\>S IDAT3^EbCg?t?)߶,{!Ht9υ=T{~Ns!eߊ/o{?K2ܭ/F.=_XW>OyI]7)-G3`#&:]"t+;$-Q!$ۡ _^.gT'6&Ppxr-ݭ%nToX=t\&@Cky9<-r@'%oٞ?ʗ{+Q`Df NC^J`t+( P@@ Pp ThR@@ ( P@!ߌ[਀@ ( P@@0W0 ( P@@ Dw o XJ IVYV":եmcEJzTW~un›du ⧇k)[qS⶞V+o3Wk'1H%,EDO{E;B(㢑%/8``N]GW[my tbR-/,Zn9HVhDg'Q~ ؛Qfu1o(83fWfoI3g~ÌwZB}npK~fL ~9ZG;{xo>(-ЍV&k7 ĮWo,-cwg3+?XiTW)^10~N>\TI|>?NZ.5ܚ8Bj@SnؒSHNHth/ҙ}%0%-WzU?M;Fz,?=?, UmET=\xJ4Lw,.6zgF=/h~=mU,k1J7pjٙ]7AIݗ!TT.++z/sŒڳ! T\?A\=8׽NFf^P4h^u} xL~#x!0kkۙž/jZ0E؂׵ʲW s]JJ`}n[YF+Mbvxeٺ%<` zE;l]DopWra)٢l.X]xˉ\jLjw mf/Z/oq2$=?kp*6 r3h#S;.{nȥ64P(絠vy953g|42SF9I (;67C#n?/!q S?w"lQSlֻ=źOQ[ћ,oB|Zюv]pbV Kxjyw݅YKe j%U= }fsh{ )R>246JzpݖmYh+R=6Xm^|zOi7Rwq1'$+pE"NgQ km=Ʇ|&F)g8:hftHE/DG|h|FvH՝SLJZ VDPcW6waap0sƶ G;am<_ic|6wp;ڤ%I.^J7c)cKZ0~\}IeRsq'lG('+o~3DY{˯D[Sf;,J&/>ض෨'&YRݎ4$5|~wF'Oȶ=1Mn/ln>IN&7.Ƶ{]ML ih$CC:0==é?,]A( sh]E2,:]\8^–-kRUM(-nmQ-?]~]#(?1̭h:k I(A޾%"͊$u~WE,{X̵=Q^m=ښ55/N}'E":+}!k76=^P؇PnYI˛KO4[w!HQuu)b1|G!eբxJ ~z3z?_YHh~v2|5%&ggqUk6VEk,3(TP. Bgh;3>;cbjf]K.~<:O•KGQOȸr *ҡ "i[m$;oN i#7?uU+1?>7 D%\^y-U9.\^el#ѾNJ=*Slo؂$tU`_Ä|歩Kuseݠ;AEUebBЅ"xO#CVc1؊59k!dBA)g_Ů_Fc MfN^-VYيJj:[Lq;nHh7F}o9uRGeWaWN,qÖoN:`C}ܱq5Z])Xe|>%Qd4xm&2bk7(qA(ë0Džw~wVu_?+Gh f\k %qߘZb<~튌yÌC(4D5q/EU :8 z6%wȥctwz΍Ǔzg8R8hrH"0eYB$ h$9A!$V^oX WIDgߥmEa رwYWM=R3k|j%팲O*Eכ`Ϗ^BNJx'0w&9.,MAaVi*!ɷg NoQ*';9v +-e:"ڮnCvfuV7P2z"3--r] .ol6 Q#WvģX3jEs܌vM2zڥכȳ9b*ԁg"1% QXZeXbI-~lVLHX.Ԛp&sh}cjLhwsԶ#ٜ: Nel$dAZg"tu^cI7Lݡ\ꝿ_Mt^J'E(GaSeNz]ϜbX}s/ @$4GpΣ{fe5t;Gsf#yOSf198#1*t baDC^XP_]hpC`ш"UJÍ/TVWUh~d>Ed< r3 )Bpq$<\(PO D٫/=1*0jF*m>GG͏a(p:_96GqWv#: e@G%%$P_hBy\Bd%ZU&#_rJ Ws > ֗ӃÞkҮyJ|)T).ҿu~BPNREN;|{m S gSѼUHo;8V6%/+F@J$B_` MسN\hǃk*)^fѠf"A+] 2 S^ѫZ,eV:RH :|kff`3M1W ۊ%QD7h$dG]ZUB(C"nQJkf;lXAxͪJbKoO%POVJ0x |^Zr&D{:JK9Fw7|M%w-q3T j{ϤX Κ: &>{HzNhWד]sgjsr8p>8 6R~ 3ѻ}:Q3X_ fT#w;`ip!i-h =_xV:8gLU93ǚEbag俦nX.|D^>짧J%"%<P{%u^"q. (8w\loҬ#Qu-BC">B5o~BF؆MzJ)k&tߏ{ @ַ~q3rեPG2\ 5dDB(g-lۈ@4*6VMErF{?}qbӬmU W7*WZ[YzFƋCDžm?hh7v9'~'Tx S.X< o-+0@cTU`"P3U s?!/%0 ( P@@(@*4) P@@ ( poFy-pT@@ ( P@@+_ ( P@@H;؄B%HR\$,+aOVҶ1|vp%=}ub+:|HlMfkdzIf}õ)\q[ODE+5Vtsu"J!qH 00I:1wDI`$+43œ(i?(3̺ yͷ G?uAjtsto緤m?aF_;a!>q|XUR?3&=7zF+k\rb׫7W얱3D“Wt-<˱ eudo[RJv6V  WVpǿg:eϙKL|k4nqVGlE+OW*[WY|E-#Wo~}z^*o]2?S,zcx?;%!r)f>>e:ݼEˋ#0moaHS H(㡁E3REAϟzhx.pQEV9S6hđAflxn4b_༼{ʱ3qڋTN7vAXͷ?9GW4+jfL;WĪofҜ @yb?@s+|(`<|tU5|D,+\~k!579q8~S%-ԝD^b3 Jp,+i[i96caa/ IDATWo+SaҼcqy p>3~G;F-/yuhbYiVZS캉]Jb4N'N/ r?\Y;}S~,Ԟ egHpp42/Aw O_[<`cRC\ 9S3Pp(TE l얝*lj(Knͯ1-ZK Z[9$8gOqGz,!yZl[G.xBok0.[B ȏ'0!rI7qa[ɒ*.Ӫ*$۪m֪ #“=|wرDeuq?T%@!2 |-"8iZ HȽo [VBVQ)=2sKI[HIg6&~R!qi^L#GS${zA4q KO]w -֌޽ZeKLl]o7FĦSď6ޤ.YUi%1kw7 '"^,SJe=mb>tjռԄz" A̱KY~~KjmUq-}L TIy`riK[HP¢=˄ԈjXkQ%35rO?kDGQESϵ6ǤG*5]E}x!rFs3Clޘe2PzmoM"rJ*E(")kۮ6e8Bm<_ic|6wp;ڤ%I.^J7c)cKZ0~\}IeRsq'lG('+o~3DY{˯D[Sf;,J&/>ض෨'&YRݎ4$5|~wF'Oȶ=1Mn/ln>IN&7.Ƶ{]ML ih$CC:0==é?,]A( sh]E2,:]\8^–-kRUM(-nmQ-?]~]#(?1̭h:k I(A޾%"͊$u~WE,{X̵=Q^m=ښ55/N}'E":+}!k76=^P؇PnYI˛KO4[w!HQuu)b1|G!eբxJ ~z3z?_YHh~v2|5%&ggqUk6VEk,3(TP. wgƿ=ާ]}glXLR͌:kŏGU_\Aqh?Q.!\Evt@¦HVɎśrh OFD̏3-`I>}s^ ka Og;ˉ~,^p8Qfpx oL-܎m>97~ORn譧_yHQHv$2f ×f x\6$]X}s/x*c$_A'mAGhu%Gcqgg]5Hͬ򽪕hL3r(ȢLM&Omĉ :8Ծ2}>۲/J ,0 "/(X%3pl&2 W961ZQ}% wcπ > !hm5Zјv <&&h۹6/8"> lVjb CS# ;Z9:@̧n.;tpo9ڱv7H!(]Mp^8SչE/q>cY~u@ V mآjHʛ??$2'A=ϛ=m ^2זوJ"{jEtB&7e-}AGѤpXG4SYanqyЈnu\,0746 q3$}(B귺6/aXakd#l6 4[2FV`֚#Ad&}taفN^n.2`,VZK75a]( |'3&k+P>5VskZ2 *)t%"q/meZFe]B7|T 龎a}d^q"I² 8Nnz[7Pf WG%e>jLωbi~!)* ՘~uݢA28u1 bĉQ}5 ilyov$Tg2JW Bt*ōL*v0zE\SG\x^*BA2CŐ Vٯhtntn9 b>+6MtuHP2i1g:H,|~ER: &I0cו lYz>N#)F4A *OvvSJ]Ƣ9hwG3@:4P>Ƥ2xA)u<#q3 w[DZ^JQwnl 0< (mJFsP/K/H V>nQ[ tquq{ֽ7X3 e,6¬K%2SHJF8Je$'8Ӆ 0`qQ^i)Z ۂ-w2cqQyT+n{C1}ك[5j\*(ܼAQ2p9&+ڱ18]wќx˜R5Ik'NCnF|28lAh PSoϧ#io-Hf7j9YKOp_ jb,,S'gsSʶ(zB&oon4} ʶY+oCCI &l^p2+;"I3=.DVUg 9yvɱS Ks;ej쵷̥5Cc_` Sդy}G =Eb]bMoPv ;0IGǝ?ԍM 9^7V"+'s͎tdn!м jQt35a/u_q^" i:molD#nl)cIZgusKJo]Zp251h\z8:(' 8=* zϤҏ37l/g]8$;B1䗖Q,̩dZO=+xƧFZeźU;M ìpf7|#ΠZofu3A\CutT\jš+ȗfҏJ#Bm\nXrDĩj>豒o9[}9{C*Zk҈J_5ם}(㡧F%]CSsUտG A ]֣ s0Bn.GlQQ5Bk3*VpdƬBRSۅ~J\6TnLZ^z\Dèu^nYm} -yeuHN e3o( ,5c*BЫb13 "F7@@@@@@@b)ф@@@@@@@@@C'/!b)O k6m#,"`Sc}m=R˹ڶ/̌}jg&xA|bl86u[qS1鱡\ PKSH :k{Ö@D{ګ#2. ʘ$`I{ܚg݃Z(~s)(4#n5榆j\Wr%޿:B(`? wRWkyk.Wjy 咫h1 nngj Bb-EFH1J,!GSFpoܱ0|9Kg&#@Ƿ!2 |-X>U|yhl.5`=C.Xjl1ŦFlq)s ȼe[(07!ƺ/_lZ*rcss¸LvZ#uqrk[!x<#x 0k-sájo<\/ Wjy׬o-fs 1G;;IC3܈`Ҵy6NT]蘴 -7a͝Atxj"iaq\a,es$dJ5T)&*p1ndef*Z/3=J/nCe.5XRa/|N];nP E=f_¦M絘%\y|pO,)~_w,C-&+ܩ#R7yEFW 3Wp%=O\R"l~J:q3 IDATD(Y*2"$N2r Jב>8a ?p<`G{6LJLJb0"Ȓu<5,o$bHCcCFfץz1#rVl,'=١p6k1p}= m,7;$e&S}'j'/p|iUdH4?a[BxLdfzOhplj>]5#%*_O=yz'r݊CN<_y4CUTJhƕ1_MQB3֔3ml%57ܜCH.)ĬuouԬ7Zั}e_sb v5#o<爐bEK:sD&3)ΘǬ\?Q%֠2"!=1D*^2Ff~ZK悏g`8~󠛶gMV~zaa#m*?#n_Ai?z8oŶ ~az̀wX3e2c:L gMYQU_=gN!Ȉ^cΒ(^|4: >&EOr[o펛%ΞuN&ǗYjo췿.&Ss4iHg0FB~>k/?=zQH-[œs4]HdJ c+ o \ !h^3QyFddLOEda؄(dBYx8B(M>EjZBdxԀC[ZXxii*'GC&&$vu R I᱑qetAAd̾͠Q:1i9}-X6eSb~8-E^<1rBF$fxrf "ǤZc+z1{mbj7jf]˙ S4Cv29N.{ŭ0cI,RD'e0'Nm09WDCҸNTN|l w3N* " ǫLL:G Z >W42{7}VH/L9!t|  M c#rAF : A/@sý#rK_]$b)"&۬pJvF&#!aAiGPvXY2漋80V 1쏠)E ACX'5=-}rA }R=f *PwCI"3xb*)Is񼉁1%sm鑍T*b\H;+(drCJHZf iY @eШQd4A=,MTVX0[m\064[˧̍4sn= (IJn a''oHG-'**۟MB&+x6HD]-wXv`ӧW U%lAwXɌڪJb'd՜䚖";fC t #($kw_vg\K[ƦQaYE)A:qf _w85A4zcXWch$}hޖ4ͅQAZ1st_HFAJ f5lnf-FdP N0?h]q̂q>2q"~wxk #}CpA"[ Uҕ6Jcq# 3­iF|Q~4Ac&;bJPArmP}1cvkx)5TGJLMGť&ٻ|^oV!Q4~o+V}|~Ƌ5,GDMʟ+zKz+W9rɽϬuO.Sooxڎ/m[ɒaɐ]|7|G*B9*rמ:U!,薂3%¦y-:~=탭Qs0cLQ1[54>5G .L QW_xt4[e=0 3x,({,zDU#tV:a7oIf*D-5]自E pm#@ƤE0iP嶑ewВQV~^dP&<n. AX_3"Z.߻*C8.^J`t#    M>Q^}X @         &`j:^u쩛_Ͽ۶ąҦoZ&6fncٔT6|A X}nP]_}3#ܐch&ZN/@kn"/Xke\4?* ^ߵOسs3_GJ.-O:\/-ׯ>?ٵS !)'wQ>2@ )_BM?fgƖUj `$}/qæo4puQg*A \wŊOn*߰鶿 [vKc}Peeo&>Vmھ/-{gi@O~Hq߿;;NS_(0y]e#So#ܮ>D'pIQ棏~e\5;3tj"bk̈7ꟙS|:oBKjN£zpݰRRκ0S]~g!r8v=?;랆R e4St@X?G6<8m@$_?n<X0yjPd/yZ?}azKLFCIjʶmHk13T{iKBOby7 6Y>|-+Xscx`e<}97qM+37#nߏRn?>_89j,aIUyĥo~]BT<,H s5S gY Q@l~7$_ S/xS _wƕP `GQ 1kyTL446>?>>c#a^arY ^~ T>߀u>λQSvqZ:&9.Օ>_=u_ylsc1EOp  .r~~gO޽csG cc.RҾg _|hﮇ^>ʱ;^3ktG_9bޥo0l<[Cǖa]ǣ]ٴ%d!|K1pxfCy^w9ձ^ޮgr*]3[ ı6D h&nrH>R} ~PTOf@oQs)Eɣ}hZ9"-g-ψWV<ι^J%9G[<ͱpDw^r&E=1ak"d.N57=;_z}tFW|kv%(NAjR߬Ф쾽fnBn &Cz4d )Uh:0 0+*W?תm><- ]UH:` |4~uz5SL$9)W7IffDy 㰅 Y)2ve-2Ԑ~{}OzBݹ:zSڭ[hN.B Y.X(xgFM/JkoB&R$4ij=-~/IOܼ}2|Bbe< t`` 'а);5@éŒ$I锫 #a7ޱnU>4z},|eC=eKRt noS2n:M+.d.4:ƨcMǎ 6l)6mΞ= ʐ_N>m?&=eD IDATjomo\knD8Q8]:VqITyGBq>{Fegn =Wss‰nL¾c $0(hh@?ؾ"%VM(ҾL Am<ďЙl#'Y5&tykۨMyC[{F 4`gٚBPKu)=l]AMJtMt|R@J wo³[.77HT)ŢNV3fY[QwDK9}(Ek߻>}=a<ymks$)w}B B @ψP o.#呏oikssF( cݥG~|nx׻9IC=b29yK݅~ ':Sqȓr\ߚ-~p;n1dBxĢ-0Ѐe.4 (H_|ټhWbA'W:u`TgZHC!X0K`{?F3`<6"#MFL#""pĖJEUx%HY|/߾mrE?|/TlowZi+a7Q|Av6.ұ2M,d9ŵXD7idHt]/sseTgJHG:Йa{_| I)G{yy|\#6>vcOxYh_zh?V͐X͝O.iЀeᰪ쭾X"-G9A?[>i c!;_N8{ #/ޝs^u"{# Zs+K5/9Fr@]-R?4b[.jvf /W/4[ ŐPtC-sdA߀8 Ժ )o&o.qv܇8ԍEF2L&'mŚbO^a_c%K@W683Y.JV'#24>ˣ/^f8)gzz,HzSW3.볿NXݫE+0>8.7&p[3]|4t*:qV'wE3ّy~t{g'&/ŤRA4zGJxj{Z0?,Goo>;a3QAoZn|8!{-aG0T=nߋOmˎ `2SWy'.XKKҭ9! wX;Wum]_.Al8~-3/;>;4GM~=}zyK.C $/H̀!'Ѯ ?7vCmᅂh찔}7+%PcWȎg,_k/?=%=9?.?o#;姏^ᴇ^xd}b0Ąk1p6+)ܵ*e0q(P]msGGΩה1^t-LvxgXEÊGf=gO.tɏJw?77͉*}rgm, /ط{S 0Ѓe\^ƞnOu$IojAxWy/o!z_ln[DB~7?u?3Wcy xh`V@y)AN6 >v=yY|`qM){w$Ǿzp37 }= +o>?du_lu]|?s~KDW>Fj~֧C_&?_x2{=~̗rNϏ_9ԴH6p\ZO6Ќ Xr7?I[L|~潡LTx;p0Ow~gʉʿ?2m.+QgΜ~m);߱|g\ٻ!{>#Z~]ح|G٪ suo=:ѻ_G4Hs_~^B2~wO.?#[B]ߞ~{^>w?C|=,I땾@$={__?v{,W`ZeBj>aoK_n?+ghd$xWr/Nc:QOZU`حo~t=/=|jH{>ȧ| ˋَ~ey>$3LDo ~YQyE߮ҟ:]qҨ#3[ܓ)9IՒA \ם}(Mc7H_ܵ6-`nuWj 8_) آ퉆>G<ϻ&{Ϻx1#0cލhZlGonij ,RV!pR|a H0(/b*D1WKy#:\)wd!@#@,塡"\M&ٻ=ݧZx1ADp<kk:C[Q!TR2V<<3cŋc9ˇO/8O 2)zB:.Rn7k{Ö@DXyB-qѰ3v_0(cN|VNF\X4+lo$(a/F;-'>>?r^ ֨s k!#uÎb۩#/XOMh|DzȾQ+vOp{›7[Y7Ԫ!Sċ-Xe C mӖ촍;gSە7=aW O# ؿEZ,E]!OaW!sydEr=SĹ[nI:i ̙9X#p{K+R+Q+sBLٴҰY^黜\SKjŧ&cלZ{HݸJ(Y2RVWŠ?aҍT:[PvWkhibTm珟cd^^q첽3E0_{WnjQKg@\O &6Us  O^>tid4Hh 5K^[!Qh׉`@ p-"@+M;sI:&W~; q7~]Q+U~ ;15ak/0SZdoMi T;>s* aGZZJA7vc܃?qEmxI0e0euѠN՟<0 r>v3c  @sZj)Iέy|Z=9WK9↪Yanj WGH5t wRWky\!B4 R ,n(\Exky/뵉7eÝZAH,?[EI6fb[%ϐ#é݃gш82 /30;|dd2E{0e0K`Ҕ̏SŗlV34(\APζ[L@Q%jmpB2oٖ*<. -Me#ۥWJTnlnnZ\IN. akνֳO~<-[V:<> 0LA4cdv./<(GG1hc} Y(CP@A~IF E,R5=,#Xqq8+=it xvڱG.!FdfXBZF"?$E3h$#mMCgblDl8wdTrxG]]"+p$ C`[d˧ cZ .o+ ˦`'[vК|xcgo1KYIv7,7wR@ s srₙ&lO8s8&ѷ!9I tLcM=b+&x<P0}@*Fh'ˬsJ2͵9tN>hgݴ=ivhL6PS2cƉ f;3H_Y|=_9-,41+ly l.L*%BrDW.fmPE\eG]R~h|0>e'JЀ7(3FMaSJp&Z:%\y|pO;}ulZƉowH+'.)6?%",'i| M9qH_m0߆F8X2)N K԰5"N i"gn\jR<nj-[td۬+$JppY^LAoԟ 9VN̒!hV{my 1)=rQxJNtՌT|=zeg\y␢S;W2./,$+Jhƕ1_MQB3֔mlB7~˫(:/5o9v]S8Y Y@;2hpX 91qQv Mw;XQcR{蹉%eLöq3&1+汅OT5蠌HHO J@aO[#3?p{̥xsdz S0t|yM۳&+?06y܍vseu7毠=vb[qyf;2H_|}&QNЬ(ǞBNodDޱkgI]C>t|9ev뒌]g:bK,̏V7/9ȍGI&4ů3#!?5r藟($ǖ 9."3^پfRLwO1yh+F&:zr]Ǜ}yNv^xR-ֶ$P$XF6󋍏@}pTTjLkSݵu<$ 5(tẆA}pjZP*Ⱦ`'ɻj&4ZHe7.Lc'ΊںB@dR2C4b ;RLOv>yܼg^?.nܿl4\Q3E3t! mK( $MoC۸ c&vh8~|ɮZ55a7~U }է~v6m,! V:~oқuYxg*46&P?:`[C ;ol1{юI8kv,ޘ iFFgR:Ri_{c X_•e|YhG4R ˉT5>HdJ c+ o \ !h^3QyFddLOEda؄(dBYx8B(M>EjZBdxԀC[ZXxii*'GC&&$vu R I᱑qetAAd͠eQ:1i9}-X6eSb~8-E^<1rBF$fxrf "ǤZc+z1{mbj7jf]˙ S4Cv29N.{ŭ0^Ü^85^x_ Jb;:Q9:4&o#7#;,'229&kxcR90&%tIT:)Xܙ,Wx0OdgGQGq <`-,2]4 iͺ@݂tH olv2Yv1RXhP~mvnHR#iP_bq>5:Od}ej6"eِV_8>F d &~'aRf./;R IW]*)ojܲNX[?v1QN7&.@7oqYwGbȾK4.C'BlML1ƒ,>?PNA4We9DDA}fl2yBl#Nl\NxƉ7gm_ ? *Ҧs*%²E VryyIL7\. #hMhlLVTo_#~<3 B4:i[ͣF4]II9vnD )*)ZiXx bDWHO %M>fs|V"X0/# b #0x]3=btLĠYa+di;+h:a,:ɍvw'ay$NcRIٔ:;F_] "-/(;7d{pzbr9l7-EQl:Qq:=2FVa)$%#EBpku!T@ Dc l7ϳl67&&^SL7ޅB UT@h I;s̽wQpM(tZ½Y-;{2cqf|oq"UO7b`EserL`>lVkC?ͣy.?Mm53)~P5lv  囁 `a C9{^wv:uwG( pd&=&P')?+`]їD96]vvge9MlMv4NF7 4jJE$,o󙳹.zЫje@gk~U<4tA9&97Z|MS?/8Vrh<-~g˙H \Zմ [dRFtOw;DJes&4ާt ząl#_M/Gtl͏Nۛ~EճS[a }y| oQ7}?F埨} ~_;EڅOFξ* ߦ8u2s$17x>xz+eyۻIσ%|. ]hgM*g K1qnCWuDN\ĩ'`5Bmunn> ʥ]~8nV\x[,c}xgX-7tź͚0}QK<-"߬^#rmj;s7^Q>"p>c|y/#a+D+.y&Z{y ٬-^w-Ds]ٛ ٣|ẺH>]B)Dr|. 8w\Zliԑʶz[>llߍwξ! S#$]lFo;;a_ǃ 09J#"{s!{V6]D Ut]QyQ0ũuj!pr%m %qtы•w6U >Qb o2_.WVI94_ڊ•47H\t_yI\: n@Ƅ#~RvPE#K_p4cNeskc$ 6ۤ@dNif)F] -b-?+ۂf?e߲/Rg|ӤJ}nrYc?1Y@%d٣Gin"y\e fmnyή|?EjL%<06:oTQ_h$'sm۸>;h B ?=uV7)xN65ŋY2 E P ]h!t1l.uF^ ׊V=Y9v]wۘ;SCj/OTGܑ3荕VsFyԞ|;}T)Q iGGK1顽|6O+x_Z{!huoD7֍[UTbQCfk&F۽BiL?&t+ɜ)4Hh Sn*K7r$}_,=Ա3qˑTN~AXul@74-j'dD:q7$&-6-fA"q 1J0YWHqo:1=:= q]Nb빘BRwB 輰Y/ϫёe2neb?STc"@*m~Hi:!&:Wu} +3\O ?2|~`avîdD[L8 XV*Pq;l|nKq|N~0_Zq6dNU h5^ E&ifG^Z=`cVM\ǃ2@#ff $᠑!@ũ UX[]ܚ_.fUjo^0T1̍jlǁM3b|Ǎ7@%q5BJ"~Ieݺ=/_Ƚv.!fY|Y\5h1^u<MR+[H $V~֠VZ'ٗuhpDj(-nApDx "vl8̪V ki t|)݂,׉i=',ŐA\@nRk:SS!n)rT1+U(+D\&0[hX}zm}mkXkh-|͂g?/XcJ_dtAH/:YQ-rc^ ?xZyǢ۞[3" x} p{%|Ħ]҆z,Xb JX~aCjrH.$켺ݷh0FdHVX%Xf_V.iíl -GS2M:33 2Y {l[ا(8Ҵ5AY!/zr 2W67ۈNb+?)c|Is o)bͣLqKH.I.Z̛Ahg;X]/|)FsHXhf)%!6W"r"$.$q1o,%MMB75 &~>_Cd~ b)'Q:Qq (n]B&`SNkq^ĵ, ԔQIhǦ֝SowHEM\N--M BT{=_z7-ȩK!z:1xlOWV rY 8KlO{ٞڽTF*_6Pօ6'60K'V)fX4B(m=eG*M4WCAlgg|OI^Ŝg`rq9ke5*x=H* m$qRͣL:Wsq'hG(֗J~KYyS~ݍ⍱=tf;,J/ؾw'&YSތ4$|F'OȾ9ѧl,#ѾNJ[XHͱ]C+eT}- Y6ƞkL t\3/F1R#.m$N|g!9x6"1k%<&5[Ӣ9c+X=9>ALC(a̾!قTWjpߩ$ʫ[$LjՐ}n §*$k@NbQDFyd'UaUl:!K aO7&{Mq3\)X2 |<Q6Dhg8 U|rGi~__+Gh Rk Ȝ&pצRmuCN5L>Oa!|~"*DwX-ٚI OgX:_uG=گIP*f|)G9N"5lu|ۭ!R! h$Ao7q@uC W9Π#4Nu恇% G;--e B1=ΨX1XIh4zQyn{qH w\K;),Ӣh ѵ:M\*$vyaubJɎ sU.H7"[z5H$2B~qOag v'8~7o1;MX@.`,;ӳL/oK%kk.[EWDFyBg@(7(f0ŒzQ7W&Y:*)f^oXa6X:L2FavT5x.YKh9ͰF{TȄBTj2ﵩ%gJ{ڊ/6ݩThFB6ǩ1`s9H@LۜNBRެ;eP WD+7]eE%=QzXhhNT /=3FKH"HbͼBet5&[ϝBzHy*~Yvwt.y9StfԼ') ]S198՝#1*t¤ljt&۰25_IOwɜ 57E,.&#;_W'h~l9Ag&&S:6H+ }7NĂ:{VP'.۾X~Mʑ=WFEg}+;f`bX.-cv J!7Rͣ0=dQ*L,jXI P(6FrR(DjB(Bp#0G;6#7F(Mp~˭LO7#mťy+Gw~q1mn l6'UEײw W=" J% AWlHhC|:yx(ͯ42gF&}!*I/L9Ezb#iEl?\eXĭ"j}\BY9{b {2c^^IlY߽&ndY>6w FauOߵs.9"ΝM_^ĦQBa]Ed T$A5A4ڱ/m3i4Zќ~vI>o./PJ :gM ;j \ "1zaxlQɠR"qCnɩqnBR0hm`[-W-[; yqk!T[ZXԮo:]\ɓtWЬkO1LP^SH;%3rv>u VL2FaMFsHPX̢RY^;]bs(8&{rxֿm,z=8^rxT>_VnQz^8٪٧z1qw29&z0X{[ 6<qyBp;aY IDAT0xhఅzAmoY;:#]g`2~F -0CKhCώ[[j?`Q.Y&~y& ;?jY'k渪ڮR#K|ln8*pZ:hYz2Q'j-Dz7Nvahp$A;/~jf̑`QZ뭔o&=sz@;kRv-?`}5^*1G7tʼƮ;<&r2-&N=+xƧj#Hsk|tAMuV.{qۚeX;jqY+odsHUu @\_nfm#WsﯝힻbQC79[ܯ~!|w$ ^q ]q3պ&Lfoqn&& v\+ eN7@rTƗM!si-3bӼNDeW ݪd`+nԼsEin!f5z WY'p :(Y߆QWW9p%̐ٛ ٣"ѨZNՌʋ:>ql.NMUsW+ok(1IzyҶq4[?@*{)J tɛ5B.9v4ks{pwv6,'OVc*iy+2M*@CHkj|vkUmZ%sm۸>;h B ?=uV7)xN65ŋY2 E P ]h!t1l.uF^ ׊V=pn_ynsgvjHmo;xFJz(5<ړ/yo}<;%!(u6&=φiK\cO_0}ޣ?uSHƺqYC,0xh`ьC@ۃt}V(mԄne02I`Mw@s~A_N;G:v&N^{9\߯#&e_H'D=<йu}}7>0~L>Z$.|>Fi91&J) V7GǵG!N˩s;Tl=SHNH^a6%ږՒg=7?*eD>Ey7:C [ۭLvʾrYHm}uy/)M'DäYaapv}a3[&u,xUuh|yiVڼԪ]Jb4NvGm"T/.i_K+ΆR7)w{2ԲKh$B] x̪IxBȟ$T#4r9p8 k[LjC+&W 8Y88i^q9]?FH@i\[393? ;2!q6^"GSRBe^4q ˢOm -PF4W =65zC*j7p"lqml#ilANEG]Oߏ! >h`K}rRh5E`J]bx{5R\4x.9Y =yjO6,B)n)̍N2W,>ˌY+Kg"G=%cB䟣G>R^aú{gg# Y|>}4W>8F[}_SR)B ]9mEx|oc{(tH*x=H* m$qRͣLQ~{~it"c{˱tXl"x|i宕lRgj('5?a&) !kog=OUdiݶbBaPU= /om)'Mز5ͫn 4&u;.T&"EI+KöXFk[y52;~v1Yǃdwfn~;̩EuQFPM+AgY^?x}kT*4X䕕 Vއx ;RT&1y9om/ /F3cYsh<,qy4(c+<[sz K[X oAβO^V9va4O=OY/plO?d|Zy> I=9t|ۚ:ާQ|IEjJ%JH92 ٓ*b#ԋή[d<'1Y <GހiR/Xx&b,‡osm2Lp}r:p2L.Kozf'É7K% sh^ZQyFiύst8(dB"H^h῏Ѩ|}O mټ?7'B/D! Li',I1HIJ6+ԸzKQCd}1haL Ԕ(b3K-. xJޤ՞{/-ZC|,#rm!Qz;dٶJg %XƳdhbHY#ӐCcL݀dFd>ѧlLhߋc-,${.AӡI|sVKkmOϲ1d^x`b\}0ꏑq)mK'tC?{d5"lDb45K yLj$EsVmP{s|`-bQ<Ø}C&ԞlTN?~d]==D{MEi ӪZz5wۤḐ*$k@NbQDFyd'UaUl:!K aO7&{Mq3\)X |<Q6Dhg8 U|rGi~__+Gh Rk Ȝ&pצRmuCN5L>Oa!|~"*DwX-ٚI OgX:_uG=گIP*f|)G9N"5lu|ۭ!R! h$Ao7q@uC W9#t&w<]h{簥zaV^b4 9+TFC|^oB+ bRIښ+tѕ&h(IuJ <p(V|<Q6Da2 oka4Zgh3>2!EbPo#i{mj,Ҟ"⋱ͣdwtw85DW٭od~QF?qIr$%Z<+UKόX}3/ @]ͣIDgć8?Rߟm߮K^NwpnT$5 xJAהkLAF',NEu'HLű e0q""!]/I(6Lݬz2gBM >%e%d5_6[NyA!9ɥɔ!8ҊBt+/Ξ"ijgr?O<=6)G\K%᧯쬚ˋ bl& @ځTjgc+yH5 E24y`aă> >&A`y`D+*lh?6EiDJex&BS0;IJPٛ ٣ ]e֏P، 26]S-+B;#3/>ތsUB;û]nf{?ٜhT i(_O-$H:_u@((e( \! z i# H7Єʜ JH'F0 f*K > ֗ړ[֓|]5*s習=2[H@DRGN;|Ļm S gSѼSPsD X-`-()" `? }xgsqj|!K(^hPR>MXHҢTjVr襕]ֈKveZ6RH :|KFf`7[1g ){xEW, { lښEE wqNU!Ӥ}. υq O3Y )H; A5c_攕I@&X3XElYFL&xt CvAAҧWٛ ٣ UDK\76aQdOfL׫+-M8 B;Ny(i໶Wv/BĹoԋ4*=_U[3,2J2F8hV;WmU|&5wTs\[+N`7 E5^AX,bV{V܅u "*Sg(-.ѭ5)T&v{9bXGD(XDL*_UYh5ے'|1jڮj& D8,JŐ)9:j;яF⃮"6L"nYCEϠeg|<QF~6TN\ 2 ζ峜.7/Bt[7axgOf,׬ϗաC~޸b?NDj^Ltt6hL #:my4ŵ=Gf&qF0 M(㡁Crgt0w!Q@L{MO6ӷS ~V5/rl =;nmEfrxwٞ(hfW>Z 4jJE$,o󙳹.zЫje@gk~U<4tA9&97Z|MS?/8Vrh<-~g˙H \Zմ [dRFtOw;DJes&4ާt ząl#_M/Gtl͏Nۛ~EճS[a }y| oQ7}?F埨} ~_;Eڅ1-o)xpȬ|mACԋξ* ߦ8u2s$17x>xz+eyۻIσ@E\ AΚTF`_yͫJc ݆2N?CSO2 >)ckR5|PrK^qܬfY&|#ΰZou5a\:RU]*m/yǗ[HW{kl܍kԅ:y`Y5~~k%iCKx0nfB6+};W]}st00ќg0dWCB(_Q(sb)7OuEPo KCl)8Νuu$*^VO$[w杳/BHwxIW7۸N(:kg8sU @ַan=rՕ5"{s!{V6]D Ut]QyQ0ũuj!pr%m %qtџ^ IDAT•w6U >Qb o2_.WVI94_ڊ•47H\t_yI\: n@Ƅ#~RvPE#K_p4cNeskc$ 6ۤ@dNif)F] -b-?+ۂf?e߲/Rg|ӤJ}nrYc?1Y@%d٣Gin"y\e fmnyή|?EjL%<06:oTQ_$IiuMϡzM=A-#sm۸>;h B ?=uV7)xN65ŋY2 E P ]h!t1l.uF^ ׊V=pn_ynsgvjHmo;xFJz(5<ړ/yo}<;%!(u6&=φiK\cO_0}ޣ?uSHƺqYC,0xh`ьC@ۃt}V(mԄne02I`Mw@s~A_N;G:v&N^{9\߯#&e_H'D=<йu}}7>0~L>Z$.|>Fi91&J) V7GǵG!N˩s;Tl=SHNH^a6%ږՒg=7?*eD;ot䫇~> [}{8J_;|)BE"_\48}0̗V oS~Uo3}}yB8e8eBѠIZ;ّ׻V?U!n ЈIF8hs">gqjAV%旋nՆڛW&.L 65p sq pbLqr g\<Ҹ42grxPn\egɥ# M-yBuKNw 52A %@]mMw\YFn! JK$m2H 59O^᣿CĎ '—Yݛxa-MoC8e8Ա[R1:q46vs5p#C#%VW66 AYMBxM_|\g~v*dB6m_1E*r E} o[hX}zm}mkXkh-|͂g?/XcJ_dtAH/:YQ-rc^ ?xZyǢ۞[3bx`K)4J,҉M /NjY,xzÆP-.,X{\ _ë}CmDfdUbE xle&BFڳ 6Mb6Я̬/(ȼf)uoaڣJʊ!.h:+]/_.[sf Rlz=4v얏>ivN0]a0EygȞTQ֊"fm[۶I%h1v O"UJD  0 Nj' ۿ,W=e >,K/"(29]gǚ#nIɡZإ6IWЂy3 A`2—g4HPof\bs%.'BrA3ƲQ$[ ~SC߰neH;D׿p*6 r3h呪(d64-VkA%M\|HMo*}.&i\zl pkzo :?ߣgXE?f_aı~TTʣX_>^(I{/eoOw77 ̛A(e2c:Lߡ dM2k{3BӐdzÞ2>t>!&_|~:rl+9_Zig{ke~'ԙc0ؑ2q<? fo|%hxix6-<<:puEaˣ E[ٚ[ֻ\8\Ѕb~ ru}rzy5 !~߾~&~~ 8'~x2K`z:md Өu"E5pnt%OoGxI1EWg׭yƊHޘ,h݃#o4c|,|<Fp}7f&_x֏N9Ad8s&z=FD ̛9M4|M-F}2le’U x#~gUOC1u& lD}2/}/Qc{MV$ V}- Y6ƞkL t\3/F1R#.m$N|gʝ?H⚦fyI!;IVc !-jzO>Px"Gsod U u3mGP\ӱS/ʫ[$LjՐ}n2Vs諐䯮9GgȞTaX,WN,)=!ޘt:6ڞdsb.L2FnTeVoP,PWawó~w|]R67K% sh^ZbKy2 90|>iׇPh4zQx< #aUTsgkF&3,LK?mV֟mb|ŗEk>')C;3 8hdbrH"0nH\6$ 2F.BI1]$_A?$:d.;֙+<"mTQ4 ߫^p;b!`%hMGuzEű"1s9,av +-U6"6޾߈lMmn`– HV= ] Pf/؝R>3 Cnmx7ۚDi4A7o1;MX@.`,;ӳL/oK%kk.[EWDFyBg@(7(f0ŒzQ7W&Y:*)f^oXa6X:L2FavT5x.YKh9ͰF{TȄBTj2ﵩ%gJ{ڊ/6ݩThFB6ǩ1`s9H@LۜNBRެ;eP-Vto&n}#s2KzH%)2^^zfDy9NjM":F?>{'>ĹTltv]r#-t"yOS\c8՝#1*t¤ljt&۰25_IOwɜ 57E,.&#;_W'h~l9Ag&&S:6H+ }7NĂ:{VP'ϲg_Fy`tx;6)G\K%᧯쬚ˋ bl& @ځTjgc+yH5 E24y`aă> >&A`y`D+*lh?6EiDJex&BS0;IJPٛ ٣ ]e֏P، 26]S-+B;#3/>ތsUB;û]nf{?ٜhT i(_O-$H:_u@((e( \! z i# H7Єʜ JH'F0 f*K > ֗ړ[֓|]5*s習=2[H@DRGN;|Ļm S gSѼSPsD X-`-()" `? }xgsqj|!K(^hPR>MXHҢTjVr襕]ֈKveZ6RH :|KFf`7[1g ){xqmP_bQd[0xgk,*"m3t}߭ &+uX|.Č̢TH@@ډ6_ZCo4%MB2 >P(bZ4Df2kx >\eXĭ"j}\BY9{b {2c^^IlY߽&ndY>6w FauOߵs.9"ΝM_^ĦQBa]Ed T$A5A4ڱ/m3i4Zќ~vI>o./PJ :gM ;j \ "1zaxlQɠR"qCnɩqnBR0hm`[-W-[; yqk!T[ZXԮo:]\ɓtWЬkO1LP^SH;%3rv'@Ś_.1 ~UelKZ3A{k@k's7|.ᐮg(Cަ(/o-r,G?nejʊ 0e >>u VL2FaMFsHPX̢RY^;]bs(8&{rxֿm,z=8^rxT>_VnQz^8٪٧z1qw29&z0X{[ 6<qyBp;0xhఅzAmoY;:#]g`2~F -0CKhCώ[[j?`Q.Y&~y& ;?jY'k渪ڮR#K|ln8*pZ:hYz2Q'j-Dz7Nvahk [ >2'[u' z>鷩f&NI /JYn P>`g&irW^s8~C̫oc"'.bԓLg|6:gx}M7Ԅ\j.W7+|.Y6ӖeImْq lj3!!-J`-A'ЖJ~ 2I8Nwl˶$ְ4-[wgJ@w}}ݻ\pE>TͬHwX>d:#'ێo$֜rKqo.7^^g9"j+Ľ3U7\N+kEk\so3[1q [І߿#^Um|Y1S<25J໱;kJ\[|6V!Y5-sgKs=:l KwF_EwRc 60%=#2 ) :?9D9XiςY6YQWkBU<2 7xƂ8{l,Nx'"`[MmZU{i rQ{]}*~o옔؈Q='n_m} %HHN g<2[wcwo,u>_7]DCP ^5^#######|*$ CGGGGGGGGGG>;De[HKw!%$ "DB61'mHl^n~z\?Y+Q6:7y,%[q1iPy_ytS:kWwOf 4W3!3* ,@',L dXI輨GhӢYfnz{h;o?Vy:zߨGwgVpWHNm/̘-8|aQZ0%XʦA s9 93Hg~3wMB[#W/u(}pm$>n X"K!HTIz%#{ʮ|X7-"rߟ [uqeky%?*{Svl>BŴʰY^ٻ+^T˾d~AI}\8 KٲdنIۍQcStC'B?Rl(///R9[XO۵%FqbŔQdBSH) rw!kg44xZˁh@|H͵cr-%5 1@:/ `$Q,NYv@W*/-9v\:b$<[#jO]kՎaՉZ̹r@Ó6+s>:Ath$r4[_U-77n(PL^p<Ϻ)8c5#WeoaR6 Tj8[rgԌ;EB49E~[ˤ>`a}n|LFYYCTk8 M@§Vʴl]43. W]nӹY* 0cbΠS[ƌKfA@k%ߘA]ѽ{]@4(1YykMójѠ8^ Z^#9usФ`֜h.]5+Eq+AE,qsUl`F*U{km<Y IDATI!#+b0'H:?ի&GPz e^ec^؋n^gU*5D`Ay!;&Զ&JxNϟ3|&ldžcKbTF.C0g0ܑ[<.Yܴx46rS枡= ,ZMTn ըiov܂OT^gCrSj=egĆc֦Q[yo{z:r<ˢ]|ds`OfFPŇ63>9.*'!#;ʢB8ñ7/< KG2g&% BXI´n,}kn^24cO{' HV۬c>Lg'[Ni'o@axnHbP$c7G7x{i#TVÙuUʨl~8jhv n]m#f$PykW33XAİ#δaeX[q;RZ~xcf+CQAv7,Ƒ7wZ@ b&h{CqpO9[!yY Of&[u]ܺVgEZX8Yk84gje5ۛm[vL`9ryvD0anm4NW蚵t-P4a {w:Xg_T~唰@8)e2l$S.&+ fÉrT&{;$1-HQriܼsz%ى24p׎HRMrgC$B::4$=Ӷ43]gt4s6kNΞU(d\+?+57nٺݨ82!8}#[wRbr"BR"vlvaDfd>;2e _?y:!qqRh~Q2mOٱ+hɵdfHt"/dCl^ZlRGdoXZL8KmrhzD )-ivzh^\Hd?OTNQbs,MN̘ь.¡C2=a eg핪y/(!s峍м>qҖ+VBTfr UԭEisSRI٥b?BӶ3mV9n╼`΃Y$FĊHkke dhEIc*>gD"0n8r7 ʊWUqVgn)*1)Jlڞ~J;e?5>D|SbV;?sF,\^#s)X(CTs]2to,aDMw\Gz-܍.r6ߐ>jk ]gXT`l)94#3PȠi[=RE*z1i=cLR"s-Ȭՠqvjf!nRi4e]?竇-%9ᘖAt {Otc D V=+c b}@ +[fU:jVͱ<+3\?T]3 fE~ $PCఒxqx@-{_̖uިVhZl:gܤ@pC\[64 )6A@0zMRkscM>jJ"!1pAxEEBIPpבGj֗3‚{C WGme }8NQ3Ѷ"@P7wL*0uw o/j\1S%Qi!xe-?ѩW6*-!k/Vz37+N81/!͋ ԏfqMdVVi[ZQg֏k͎E듁a֠(J<޻ Xd^pz,ܖQۄ(l/mn{;\]NJYZ2 Xf`t uSyUY)9W*YVl$""`x8B[3|JaQi)r=7I.)";O`3aMӭ8;5FD-CB3xdtk~*ioKMi;mb#D/$36Z.N8?3%GT$ryΉ D"fIA7>MӃ~dx1qzQ5-%,<0:Y¡@SStpФ:b]9V Z8m(U -t3Ϸhi[b~8'\qb2>݅ ADӏN)Lr}ݨ^@:<3c &G5Igx8ur>2,o7`N哪wd`N42n(gu𚼍=ָnADiecf*S1-cؖH'srcB|N|T9?Haʝ7 g$42a Hp \(L};dykˠdJ(bC4rM-ʼvQE} ܨ % 8fCSe +V?chE֝F ~YmAdfHVdɄ`]F3t uSe*O++;SmIe-2I6dx$ldP,.)s!TcUKN"6ED_O0z)4iG-n p9b(D뢎|f֘ H"!؋}ejA]v¶@.> % ]8dϽFq']3זL&C{jEt꙾|zƐ„BX5 `2Ud8A5&V!u,#,Ыv]012Rx։ŵX;FCBְЌ5n鱮zVTT?͚= V3EV ֚Wt:j2>X|QzKp45a]8 |3UHgL]qMK"B3T-(0t_vboRIٔƤaIA:q7rT 깁Q}dΆxBae%#K$o./@J؈}ԊŤRB0@Xfcza)v 42 lYer)#Xkц 3.}P6m0d n%0zhqlSK^z^*1 (KtD+P!Gl!0YiK E&K1NN8>+AbDrC, ;kTy[b$-0fCI3=||BD#Sa7pNw^w71T7+;Qz> <4N|\iO GùVaT֌ీEChhE9yIhq#*{OAbT0"vTjlNTͬHwX>d:#'ێo$֜ڏudF/qǽڪxz刨o>1T8s [W;qqͽQluj\Ʃ7l}BxmW٧-}"dLȰb(Ƃ8Q(qnΫᛎۤZS[.g,ϝ/2df\NJj1,>F5w!L΃w})L4`ڟ{YQWkxdXnX ߍqXND*'۴^Xay$CUX1)94zN:16j۸Jbɑ,E 7.xd1Ƃ8Xp|*n\+V.>AxjK 2GGGGGGGTHxx3ʵ/|*wum}/| |n -<WJl8nuڤo}iWF]5z?yU%4m}{ڙy\G,WwƤWɤc7N}֣7|~~Q7Fܾ=Z&E5g7E˴Z՟G+~2>0rv7rۮdokV*1YZq8u|*vCg3=ڷ >Acó/vՇ;r?5ʃ]O<ſ?rǿxޠq=~͡~ĮN%`}߹?LA р~wi{6óLHg# ]]G^iZWrۏ.Tx+p-姾x{zw2<?/s'_89˯Ұ:fXkpծG [~ҏblϾv/E `A`c7/iT.̏TsS(1#zhoprnp^#u }~ڙQ뱥ʷ<('^?Qeܱcb{PZ"|aGo_]p})3 9:JAh@ǞO\7Lu_|ղ9jva$"l0j&{+//JF 6LU޳wiYsch|abԂV%j:6]okWnd|"SvkZ=vc_{`g/k){7+'~|U>k5Sk~x>^G=goc|C[2cuMd{gD7_(jjϾdOA+`>=54j*-&ά/~}|]Oï_iR=ʭ?sl3_5lLq̖aäڅ]{ vl] <D_m_. g'F.h`!C*}}>iMgр ohݘ"ht IDAT~C4+#/f,)ʞ{ZTU ~{,G}᷿>sSTÿj8ޅd /Mܗk{ٱ;jup+^vBMm&q7:loiPh6h=8L,,6>{"DF+Xܛ@" N4gox[@(:cC=glYNG;;ĄeloV]wYMِP6hqQeO%)1{]. ShY ۵;s l3CFn^*au+1 Sv%+-5 XiWUlB{v'^K?+w'cI1spܧ?>'\8i  6&EcU;^Ss>or?ˁ˞=^QIޱaytx U*ߕ\(#h/ M OZx[Ov!}?_mY QP@T<[*.E~`t~E 7V6c{?|=a<~'{ȑ =c>qv0q- r4iwkL27LzT_?Tse*xǶ' X~F3n~W|o+Y!_>C{ >"] 1^"tw?uДBgJʉ׎Zd.YYOJvUި=\cy4V,` uL+oqpp`']8yoQ!2^{ CcTz`ntAW+2㭗H4f"ddwv1e&|TE?/-_xZg?$4ilb|~ﱼM^ᆧ HޗӘLof$Yٲ%xeqnN y?zWo _f_1k[/)a 093a79' diWivFe8%X_X^Kxu@o؈ @Z-`bqk&*.}OַP[zbSs ϶D_$NqodXG迾_$-k^SwlPݸlX!PxC-kH"N7Hַ!=]rJw}vӽPXd Wh xFEM!zޅ)mp#fڊ몤%v+/a:e#P "b Ka /g\ř/=}Ee'RgMCWRѥʔc 'ŸΌy%>6x9rZ ꑳ]~) 쇟:=黟/}vBg3P36bߴ26GCҏ^**؇~:=0:y%8}]/+k5U(s ,lZaf[!s%;`E/Li6o᥊(̰_O&V鵫+F!6=U͙ G},+<+/;ϟL$`IbBoIq7lRci_@ \zn*b%ЙYG~fG-U)>{ V~¹+{CYxg2>c@XE^+'8888Ahi ~xuʺǟҟO>MU]~;o8ɧ>}-)ɓ:wᯓ~K_=ґ ?n\t-@@dj>Y78۷ɂFZ/=?oĘE'嬨yRUs9?I:*~7:09>̣}P$C6k_^Lz@ Lp+o+f&XL]hyoiz킍P>IU#}{;1,[tG}5=~b-D +mk/$Y޽XE㧕%:|jӭˎT0` ǥ<_4`sU0'j?o-So'~r}GBjP˩CBk<#z_տ~HTLVLGo;o _ռ7oԯ6~oW~-z/1mTڥiߦ(O 3uW̞-o^P uXo^ڕ+SBVrn|\ԝ@O?#:Q7=R'W{Lcϯ|y ZJ{>ȧrq櫃7####b3 riY N#_);t:GzuD:w׏K.|Pt /ɢw[##FʯQp+XYOр١oŏzWxqqn%ތPoho,W+&x (ŕnGЕ=_x1WSyÉqppppppppnv]rqppppppppp0#O1C7XK'}S=TQ]EXBJ "8A(d-}R p+O)$mah .΢Ț\\r eIz\:)R,L*\;! {@hX 8,@',L d7鼨GhӢY-BqD#Qׇ]FEu@*i;orUckm@ʦ<aJ 'tK왚1lBpQ8BdNQ@V2ϭ[cwX{k,a/|V/UڹA53>9fk/HХ7eZfkyh.W7O,bvBg)-]cF^%3 ȵo̠t^ FýR PY㵦YhPOb`/xH-BA@Eǜ9rhRBskN4jAԢTqD`0hd#uՃwgڒ<8%sCZjbpT ev-Cv2/2dd 1x {1pS}0V?tx8Tj r1%2CwLطVpr}l4!o\"L ĸ4%ry\iȓ:[Ey.b+wKRa-( dIEQh~6? )'z;WJd6/;;%6M'j֦Q[yo{z:rzv]|ds`OfEP|h3Ӌsr2,iA>)?m'S ~$zfb[)z,(-L0Ȓ*u˯yx9`@Ќ]v`dC?)d&p#Ym0el:av r>")iܐŠI:Zo{oYň'ذRwΜRFeÙ$dWCC״1 .wch0$+1mqfF+r1ԙ۸"19, p65nBJzv}we(j?ݮㆅ#8&2N+Dv\~VVl0ݤmoh;I-rHb^V7ֶ>z[ Q gEZX8Y+{_i3pFrfۖ9tNA>bog} aadLj&'0SU7f-] ̀wX31^-Y|'<_f9%,42Nmy L6ɔ "pILFzzgFK㤧Fm=RT:\/7/l܄eى24p׎HRMrgC$B::4"$=Ӷ43]gt4s6Dug*Qb2z؉lnTkZv-~F1IDX ;vO6]0"3bKo2߆Ư< XjvŅJjТDI0y<9,gǮhN$ג!щ*bpziIJeci1.YˡFV)<0j9JXzqQ" -pMӃ~dx1qzQ5-%,<0:Y¡@SStpФ:b]9V Z8m2(U -t3Ϸhi[b~8'\qb2>݅ ADӏN)Lr}ݨ^@:<3c &G5Igx8ur>2,o78nkaN哪wd`N42n(gu𚼍=ָnADiecf*S1-cؖH'srcB|N|T9?Haʝ7 g$42a Hp \(L};dykˠdJ(bC4rM- 70);w~qYy[4X?chErɼ:@"-n,;t1z # F3d2 5um:'q2n5 V]kZr#:-ops.L;ë*d‹dljHδ}|mEX;DZlf @Um@Dq8Thκ0HP+`@AZXC9aaFix6>< ͸3*\3DTȇjl߿bH'O"bmrׯdgPxHAh=ʁ K8ٜwdbEP|f֘ H"!؋}ejA]v¶@.> % ]8dϽFq']3זL&C{jEt꙾|zƐ„BX5֠VWhXò`BuyȘnk,KhZ'c{`>  ! Z B3ZָaҫǺYQQL4k7X3HY}Xk^ " tgcg@_D}TF. tuh/Έ"WW_#1Qw5-^XRITrs%"I'eSJBe$=CXkuS"4PXF99t Rw,}vw?ۓp::*8c#Q+z|nVJ IK Tam7i7[Td+ZP!fI?e&V ԏ`al`D.gl@B9;VI¤) vdB~Z(難Fiz@LAn,e{)zt*H ,-^`C݃jN$ʎγ}0gŦ.)J\.89E (`SEmɋ H %E[T3G \jbLe%n%: {yA;= JޘƠRYܬhwGݾOat24;-swÑp+arX$$5#g+5x,`idyP+ ';GQNj^Rjq3Z܈S̤Oڳ ܟpF#ZT2m IJ gɌ Nt,`yq^.i%Ѽ^Kۂ-$w:#I\gUy~y{w=aVͺ݇%ekå)y쿻sr1='iþ :Ivv J4P>qFCE, haF977yǁ\* _͒Q錁&@A>4%lQ^|"#3I=79f6,j؏WNt7w9O]kY2ߗ 3}\kDƒXĭ嵄Œ#9};3=E"]buHf ΎΟB'HnTPԜ?h,$Jvxմ0'ih:?w V_7:*{#DKU17@I7/6n[< n=;)|W' +fn9 gF[:NJrnVs 7,,]/jYr251h\*q\(acFϴҏerEu#?gH{0Su5Lo \!X5F=չ?sް mx;]eڶ3#Ê![ FպU8ʇo:zlnhNn"UCR>wD\mEbrB:x_- N@ gQdMg.w.يĎIM兲˥#"ƭ!\r{2! QX_`4`ذ:aaZ6 úM @EE:Ath$r4[_U-77n(PL^p<Ϻ)8c5#Weoaۘ)g*5-gjnpQwȜdӭR eRc>X>^&U#n_smjf|r_&KS+eZfkyh.W7O,bvBg)-]cF^%3 ȵo̠t^ FýR PY㵦YhPOb`/xH-BA@Eǜ9rhRBskN4jAԢTqD`0hd#uՃ*TI뽁Y$#?1$UZN({NBv2/2dd 1t/NmMXhw*"0 ȼ\Lj[X)?xS ~$zfb[)z,(-L0r-x.A3vفQ wd:|qE脝vb OI0(L2zsdqw6B-Oa>9_W3Iڹɮi b@\ ߅Ѷ1ra6kIWF9ȑ83#ˏN k9Lm\~Zu@UH!g=;f澻2TnWqiy| ";.?++6nRM74$ jfbO^\Eʭ l qX @Cs+|qڪx,ܮQ\ٶemf+lPۙ)wHXgGچI1LT{ՍYKE3L |CuV/OYN r[f)&@2bBl6*GlC*8Qzq[n. n:7gYR^蜝8-C wA$Մ1*w{6?I+Mo`](JT$E,۲b;i']r/b~I.NOgǎdZ$"EQI(DoD$KPݏ 177̛v}-Ȼ_-36)#b ԞMHd]tZnn߱ˡ?ӻju[+hpCyId89{WFJU;8Uu I]B^C 'NB0׶0)2̌Pb0RIdY >?Pg_4vSPqqCR*rlq|DGR33~kcMլV%6EsPKK(n˥@;2'F?+q|siPfP*?58s- uE3}[R)4}L)E;mt/`7_3zfCe|zǜI.)D'ˡDX"^ 򜴌hxXv _P,CrcEMȟhrZffvOyFmU^hN$(̎@*pgl-BsKb02T=JT\_È}qhwcj8,:,TpwԚ,~סItb*+Yg?|l^"4(dFn0iy_XLKC]jvVQښ=(o^eZ*Q#4&l1%ӗ∜ \}(cqzJ$"3 _FdVU>W=|][lzaÞ4SWErW:咅Áf}1yXn͝HR_*:r^L$ĵ۩ v%Ȍp8o>*'Gdr$!I2⭻q11dH=!_RA+`8mF|Z!rlx僉QI\~2mR2NNn{ [\Î`Rs+60Fjw rvQXivɫ_\O`˰vӢRsJ Ә*K 30*]eEdG[9yyv#8B rɷWbIVɮ(׍-^iܟPFk87DHVNq(Pai[;`om ql6J̴X*p.Dʓ;eQM~'X,VBE']GQ:-V<; yI[c .NxyG{>gN|9Uٶ5ehr08@]FrblZDa2(t!6F >0C3Mic X75TZB]efxрgN2" {RY-lHۣpnᬻqH o7:AaJKIJRN'Ǔ2PJd>E(B|eێ/q)XG{6+>}{f$t-8mUO LF\ 0İ@}{>S1=$7VtoG׼! /vEiQֹE'nGD[(R!ǹZV> 1=V'Fefg>d;Ui6X:2j\$r(pG-h|ZIҾZod2!r C*p6D3k*bWZ'tT']Cv$rNGA@Ed^Hu}:/Bn?s<[\졇F IujO2hBAn:xH:G>qZVH?tob6L/dҵ;5J']ȇN_8i;Obu7 ~n quӊ8FjuSD2Dx $2*f(+:fi2C#NzFUx÷ĊXqç7eJ٦n-ڜuq #>I|P2/ ]Eyтmc3ewy=y4\ F 2GizyXÐ ؅p 8 CHԈؤ̲e{6 /Vx(=׉P(+ ]iwSB茀 2tt)k}Kkb_T1sߑv\' FfÓfsMޮw^22Q(Rʲ6WTmDKܢ][#*T*/ٽ%mU?WkNNrV|%`}~j֒VNN;a{\UT-+MXDwxlBȬL2h̨ j|t-6\ʍW]XY&^<^M-lC!d؈J*,'&OsH} `?H*9|~KbƄ1*-I0ӶDMD/tYX4Ef0tVB-|߰,3BcLᗕJo-^;}&Ό[ IDATQ)~~<#{?l W%3b /&k|8kZvbdUr`++ʋ5Ny67cٍ5)l<)Vu V2Z:B͵e g\^Q"Jenɍ[7eGy-Fh. f7,Ɍ.KG !R>V_lW߻>ٺxʍe3mM}sÍb*~/bj(rIKkqDe,40؂Ѐ_VVyYk" p< qTEuմ "f8 K-WԎ_\VWE4 tcSURz\yO>8!]xVxڤ=/KJ;MZZ͓P6]WU8l3..H9dk;g[5sq]c_`ҾKڒ#sK|EdliCV`dp$[! KP2?%G[.]Д<\f;;E 6!=ܔlȾr\YU'uݜܹrX Gf\OG̴nfvYeDGJ!rwx hmsYgG=0ӦvXu3;0,cA$d "facZvؤN3J>ϕ~O5vcq/fW}2 Vn= +pƦ"DG\d[gT]ùNÒopS+x,ܮQ _p>2(Úa ̠EyBX\`]ijg.j!F "wϣՖά{:QKwG{ߡRSp;@pCiuo̯ Kd[F>&&JX CvVX;JB[|ⶣǶYw u!l 3#Fy7:YQo~4'F=_W Flaf%j3%*:?0,x88[3Pa5-DB(X⽈gV^lѦ;sV>Ё\ƲFRo~RE aX[ ApUީ6ߞ"]f`QL @ @ @ @ @ @ •Q@@@@@@@@@C|'/! •':]ױH V`CL)JbzPup+3"V\9d/5++3;=CSw8(n 70=+ 8R|‘;P TPDcрs2Kʊ2eJ+zX-;0.xw{oIp볷 D/ J)[:qvil}F:Fni~ohVz1_xC&<}dꫴjجx.ZJgG{洮#5)Ξ6 E(B"KkDw 4+o/^G݃(U"=luu \W'OPp<\ͅߢJs`sSXe!L)VdžPqn6Y:n w-wtݹTA tK"5"~k;%+-v϶dEg"S`Ft(FzUUA8n+Y;OKWfj=ë+۱{\SHzqk XV;C4 Xhtрܝk,2GUyE`#"Z[7]tY7'+]V];#  6@4iqްaoa^,sRn,څi;;?)UŔ÷V-vj戂 xigZ.LJSE i,To[ޑyGdz) `uWZBлLF`Y}@4hieUk3*a7)8x#I`0 06g渖_g"KD ??SXWȑtͪvz敶i#&-1% %%bIf\⹘Ĉ 5BFQJVA^-pXˍFDbEF@2]LKN4C&OT#&'RE"Gv6_fvuwU:SMΧJWF/.,xph^w\ʫ{hà܂?_|^_].gCab*7 #$[RQ_'p x^ˎ/s>RSlaZVY#jSRÏ0MH,)JMũQ2.U7H`ڢ4dVŮ˾n| ^),D:.a9*߄}B,K:"3E(nRgWxk TNå-rA"bJF:;G rTf֒tX˪yjm9gUUXt R]8ceѕqEa86(6=sZZ|v҃n톋"8 2O*dnfuYYF,iT tLi|$[UÏQ8?!5b`PDp(Z3@u힃+kL^<եp kiog#v,6GYqbIZa 58Xh>ru-5aʠtXέ{`W~Mi ѐCD\6 zL$Cx͘ؤcrzplʪEEGi xy*Ǎ)qlM/jWׂKՑ!2c2")v@ل4ZZIصKvn3KVwH[ 7OgeڹYQUװ%5zD+,sm "-#UD {@S]m7 7?$E"7wN,5366^XZ -A`Q+kS=N ↺\ ^>##a{b28eV)KSY#cB:~rQb^YarJɩްzt$,-'NxbCJ)mm>b*P]W$=%_޸-M_yF %U>pNՋ¡a1pg^eE57zj=TF1Ǯw("Aq]JE,81/Iˈi'5+8 2O*7V+e6nvm}n_aIzڶ]la I8 >wpƦ"87DH*X(C,x ۃ|D1#YIJ>{fZYY6^.hXpƣ !26K{;gKu)'rDe1z + k C krq3kuM}sND! zR} iON!uK{eر9n*h|)IUY c@تAW lJ QɊ)> tOf,,Lb@442(+ ),4닱@ukDU,UБb !N&@%]C,AfM܆ӿ%$Ƒt}U9q<$6.+ A Ioݍcō!D . Zi3s-׺Yg{+L Տʐ@L i•qt>pfȾH⒬v[<9d-85jTôlxLؗCRLK^: % N~[k_Ǻ8vzsGV`%|ТRsJ Ә*K gPU<`HdG[9yyv#8B ʅrɷWbIVɮ(׍-^)ܟPFk87DHVNq(Pai[;`o ql6J|X*p.Dʓ;eQM~'X,VBE']GQ:-V<; yI[c .NxyG{>gN|9Uٶ5ehr08@]FrblZDa2(t!6F >0C3Mic X75TZB]efxрgN2" {RY-lHۣpnᬻqH o7:AaJKIJRN'Ǔ2PJd>E(B|eێ/q)XG{6+>}{f$t-8mUO LF\M!.ypி+ЊFoߞTL4Mt~y8U95ous inZFuQ TqO!xCLIe4uQY,ٙO%8qk /V?㡌";pn \- y,ܮQt,{vO){.h0g9H4|\<<3"V?puBoNZxhtƩH}㬻qf-pKVi7Jo&>PDxG'!JήsV$$EH|Q4CY1h6M)B`q3b«'VĊC>-)W§@.8]]3..adT'i_װrjsWŲ>/ZclFӢ,.'Ƒ_Ж(D(M7k2!p~aYVUR[,wϦ*eء: e%|5|c!|; SnJКWÑSf"9roiMuQ*#u.;21ގ6dxQlxclgV i _eoHz|o+40HdjǣCZ|TT4 (ŒiePڊ7+6Gq0J544mvbR.2ZfEv"rs;e(gzR2™׬%S(mPb\Iσ,s5W8X5 q]';Frb#!8$~qwƟl*ޗS54i Ipt;O#I3tH荽'gf޶_%JujCNIMӂFű!k¦_R|Gz|\[QEsR 2MΨKbI(;ga߾8;oݑΧGWc2eIQ*(6.46+l^áR^ᴙ#H6 R!.euLeGGmXe}^@[3O5rdFeywe؂=ŬśWoK"GKFLNm!05<.~9" W M#Jjb1!w-dK lȜ-bK6IJr8Nivʅ՗B_0bfW鴈h o&\QɠRX~a-iZvr#(6xsSĬlrrzq$ 㺨mYql:&T%+8cZFfMe*AcFTfT㣫o1Jg|Vn m̸"6"htnjyg %;FPRaA<7|CVAR\87&UUiL ew]'J/l"z #¢)207Arn}`c&t<5`,CLq}mQj, ?ںHДgTAegƨj}F~t+}zKf^^MeukθDʊݒ左o,k˄ZNZ]lwEx@2|`'iE7>pf[Eétq4@b3h!64s#`!|ʹ|pC:; đI{._ԗ=XCw!|'ۡlj.pfT-]w]rvζ]k_wŃ㪻"ƪX٥}I% G*\WDɦɑ҆R7/HBZd~JL.v]))+yxݹvv?l`C{)ё}&xC5N&9Y3'X Gf\OG̴nfvYeDGJ!rwx hmШF%U{9 Y\;&5t]{aM-켱ffw`(t,cA$d "f:& 2&m~pQ|}2!İ}E5꣘٠X4rI(X36e!Z':ꊷuD}3|ZcvP{,A] nod.#eR;o%x$=wQQo7{uf݋׹Zng=zk`}{-%ZtKxH+M}c~8 lX&V?'200Q2l;BB(ߡPb*g=*C][gl1:ͻב̊xy<>荾7r9BZto(5"f [66+Q)QqfɝgQҞ1UWw:pG!h!7G9E;=4۴c -w2} ,.ʄe3|B(Xw +רNE t7(Ոd<"w(M?neL47|~@4ύAD&|..g\zUp~ K/ oHtiA]ܥ9ʥULLcvg%3)훟}?V5II_İ ,TaMiۋv76A@@@FVgk!(b ?Bϟ?ȧ鏣ٟ_^A-/_wƱ#CǁsUR#'#1/hL+_zG_=VT^_/S7O0n|e,4"C@4 RoW+3|+d 0u~ָwߑ_۱oFk$R\JKּ} {=߃$`KdalD8ۏ b7j7E%U׏UߕRQλ'߰wS(L7' ( =8Ʃz_95 1,f3KI=1x؁ՙiY3/I|NBGOu׿Iewś_kh6,v{=uU_7Sw_~1`Bb5<ҩ_7M(mxIAѓ K"(4 PD)J.&@8Ҳf"ܸ%M6ҝa )`Z:e%AɪRDN[LyAx [ m:/G7 5L*Q3{~uq GٵE2KFuʊwGWuM:|/]Oc`Y$xWƀŀ/<"=mܼXȺ~=?qN#5̝?z'}OVD|&wΛ?Ч?p}iס֫]{֌HqO~k楉ӯj.V Ⱥ+/<0l}lݦfEs2ns5Jm+C`@Zc7)“Vَزăۊ%aoq"*_s73_¯u}r˹Kcct4@cU9>M@@@@xV~ PYuVNOIK%9%+"DNKMƃo׻w>tY"\D7jsϖ&ş4Qg+%~+H?&!;fbpȒ^#}l~A Rї>}?{wqo^Gb#v=LOrjPf0(7xj}":^$=X۳ :sQ^/'<1q~xAg˦{wG 4~fƠ~^m2=XnXEw`ۯ;^ݫq|9іύO}Ϥ"Ei!7'lT0XS$?ʡkwTT&xK7~y]\Y ҡ^QkIg+j7);*8`}' ^[lQ@@@nGjcHyG(2Dv"A3ޛ-O4czty}|eG-|U_}3>1>B+_m4TD&AῼrzTiKzxbjUݗW!3>4SW7P{6#mlhث!=37'u VZN[Y뽾>jf-ߎsQ{Ժ&[/]n=w˪*$͵O+,d9'XG!NQa 8V9TȈw q+qȳyE\ndxm9i?t f< "V+z3ᥳ_?ʃ"Xk=7tipH/;_>hz 974x@XlaQ;7'}B xbQ3Ft[f!:EU5}+HQA奇S?{(u_G.l+z'E_z7ǻI-ӾQ _<ĹV}p'8>"n3z ~+FGt"3NJV:Ef 4xDzY_8ؘ5 %Z>(6ǰ_uEBܧg?vx_߮cn qWr$HT; u:uZ^X2κNJ`ܽAs@-d42j}4/e29zv8)oc3WXɞr/$tg?IpIou~uOnxrhxA>5J[t~~~ph7-6xLzz`(A @ @ p#n]L÷^bkG >?;$%'ͻ &%%C' ?5dm#~睤t>y]?r`)2mԴ_?QwZp=$)޴Ņxcmi|!7$ꆞ` "z8qhhs06k3~K/76pٲK\{9V<0|ȱ8-4Rq~㬻q*xڹ/X.I1\ٴv7_9Ơ  Qm6 Dv1A0Kj_2'{ 0 ؇GOd*?mOoKHʇ8b7 w nOp$G @ @ ̷33b܏5;;U5Yn<32 bM>)Q-W+w̷ѵq|~94B)5v"oL-,b''=]ݦJ/1h(W_xbC#7 ݣ;257:j$zJ * lઋ+5{>s p$38W"{T@LjBFwՏkI 8ӘL&d6-{m-??P4f~z=bۑQC|n@cUppB.2K @ @ p"vIO&t_~ަ\略|}(~W_8a(8'*"䊧X VZRk~)cA={kϿPA,+2SJ]vB9g(3/2)y+_؞‰+>Go6 5^ g</Mb2Sصʳ×u?0rΜn3;'xo_aFgpnXv;N[?A &|:^ЃYYw-<|.&9_r t_iG$C|;+/?_xnZ7?'__?Bz@׎Urߟ}G1Ls?x+@-B HK/0챟U 2 lQh!c73VЕOCw^xN}w_їet߳t7{h;Wޓεu_|G+ir3}ſ|Tt~?=h7>?-~ɗ~O/phyN\uQce?'ė)~ى/~@4gp^d\ꪠFuHMu9Hb~)N4ӘI76[\߽ ~ MѵP4ߎa7M3ΡGlE4 ;=`D.);a5K}+^m,Ѯۀ@WN>_Zڎ\WD p /˄G^}ُx3h~HdmD|L w?A}妓ϗp7:2NJen!xKf`2{W@@@ ސI3O_<)]ۯhY幩\Ύ i]G*wS{;{v$y+-z- qM Ú2lt#MfȜ-dlc@f6qn ՗߹EXp)n,vFo+ψc3idaCjf rWJowu/>UP9uHeH(߾ NJKݳ-Y)ؾj9)2ҫ vm_Zrվ4P+KfF\Iݎ{M纽(zrk XV;ut XhSErhWl޸v~^cŤ ="\2+2IA7ߺj4{d=3x¨)  6@(Y"R1ºBkVlճ7Mw SZEuX^\8،s^<Q@!pFY|Y(C)x 8߫EtkohHBiIN̮.ݸJA[Lج+Jna0ev k(U8 tA1;E?|8\φ mr^^^e-Z{`L <_eGMs GG__-Y og|x~X]t^NIy?p.k$"8c^XPSAS==e\D!nX)YEi<ɬ]Rx>AM" sɜ~o>!KqHzE~Yg|\x/CAAQ6?.EԲYoBjn66Ku7[ )D6Ţtv(| ʭ%~UsrΪuR]8ceѕqEa86(6=sZZ|v҃n톋"8 2O*dnfuYYF,iT tLi|wn$[UÏQ8?!51 O"U h55WNbW>֘*xK  kiog#v,6GYqbIZa 58Xh>ru-5aʠtXέ{`W~Mi ѐC%\6 zL$Cx͘ؤcrzplʪEEǵMُ4TM1.:R*w ^>7'̮.)*WG`ˌF`O캁ڳ i̱kNܭ;v9gzVbren!6?/),; 'gHIsj'a7飳Kkh w>`maRdޛ?` Ȳ|~<5bϾhXcRs懤TΉffRyً#YkW7 JbEcmIPPKkهwd$lO~VҠ*e)T~*kx㞱xLHvS/^:J+1 _nYI19P/',-'NxbCJ)mm>b*P]W$=ӧt+J|y4M_SjJ|:x[:nrK|h-d5C3lfFO͖(&9]R$2NqMe$:x)xs2a{ h7|4P,CrcEMȟhrZffvOyFmU^hN$(̎@*"2z~!Xc!̣ UF.>lzU;W0bh#OގdEPTWO'~r+eo)⏷! Ț߰eZz8 IDATP{eFuC6-ƗP Sޘ:٭6#uGjS6+kyT:q?į-ٿt9Y#묆Y ( Y $dϐE';>^@zfaK5>"3Z,:q_ۆ@iݭ?:Z&Lq% mI^k/{omfnXF_:6 ŏGΊ j⦧Ef<|kL_]BNV^Kjb ;b5w^r"*K}}ԪTy1Q׊@lbzJ_ H.! 3&nHߪ8 ZZyk dž$ˈƱĐM"wgCVpڌ,\.BVx^qs2d1P+,Cdڤpe8#31$+)Vl4aY N;0-!xWNB eáam5ms}c7G 8ghqihQ9iLp%q3(*]eErp2ӣ𜼇<^hAXIB[+frZ e ^qdWFjC /Vx( H"$ X@ 8Y|հ6ن8i6ȿ_vDzA ,8A{s۲^f,+Dˢ擮#X+VNiGwqTKc's=z3}A@윪llU D`4BQ9|DpGQ9I1\6F0 $ꆞnXV^H#qM!t&`uY}H^*-L.232mǀ\{y#RLνEd{[DmSz\=36۪~O]\_tu&`Ky<8pq偶.2}|bzIng1ގyC_LO5p2sO4 PBs| #bz(O/-bA|*vv\lx ue؁sIjQcv24h-ciS {WMs@k!8A{q +:s߮PV!; 9ڕyt\d_ԯLZ8^W^[}($ZOo_*qL`;wg;,_mY!Oy{o;gYRPJJ:Z:(-tP 4@d qwe[%YekؚնR8ax{{kmq񎲍$śZ'ǖTq=}FMHzBVLbΌn J']=1 NwZgce^.V qz݊IOc[]e9S2>? QR6Lq򉊉"*\ C(Њm&M&omĉO /69VX`LwϾˇ@_(k4\\H(R֞dVU*. eC7qB4iM-((aW42/u F;L9atbrx692 )<oen# h;@KCzW9*.ۡt% WȰz%<-p*w⪋" +b֔ H!Ӫ}vs8{{JzH #b(:lDJlՁ|~8I'SRL{EN͌Nm d%JujCviR0!f_RxGz؂〾ΨUgxDkCs9Y;VqjlB!!OZhrFzBH#V1LVo ZDo'&&7Mf^ V2FV"3Ad&ta.[GGD-P3V E?QYZm$vRŖlTzdx*bwx1HG ,eUTRi`3K c 3(06;зSL0 RChdUE.0V:a߈^ ѹE s(lAQdxLSm8H"|~E~P]TY̤Pq+(=h'앍SΊH-/]jB3#Xt:b[E+o}o=LۏR1؋2B5#ݶX~#&gIjL(w"nLfB^2m+p{CM#Ps 2ɢRYaA.kV(䴚X_ eVE?8bYtx,w(fxzT~B}Qq}E Ѻv9y^ɾmb"xWQ;k.H+ݕä3KzpD`e,40؂Ѐǯ^o]HtKuX" p< qTP#ZjǻDv3L꙱a9VE1rHOtǣn)ʩܕ f k-&(B"{c#TFra%T8T1! { C#nzcfFiyľs!bg!hm rs%bb[i|nijpY]{9as&<&Eb]bR \p3&akuw>W~vbMALhI-ltz VM]#F\P]sPE}fkWwnV//bX\ͬUް`}&3H[( KMx\=~7-.ʨۋY+=/Z%pxa5@5./!Zj?{~­B/u#nIVb;"/LVM lV__5 %!֍BuV>r݁C5:Q|U3#Fy+:X~:']#綳/Kxq%li%(8=L=R;pO-i^u&Ax5a__e%*ގfoGnU|DўEިJ<6Tn\FAfBTä]XnǸZyڏq ?Y, goyQ,n-c*Bе@l(EWޭ,JT# p @ qD!        X{\{X DC         %`c90` 64AThnYն=n|aqvbX U+1<› ܵzMefƇss啲 Q]8RP;s!!^ʘh8q 1'1'/+!"IZ:Z:)Whت{y Qzhu7u_iU,(kw8G+Ɣؒ: ${gZ45ꅙ1md~Dȹc˯҂B\8/kTggl_[ԩ^JX`mnD&^h _9҂`F)\@<^!޹7s^UA _ƆP1j :lgiR\ߩ+A/\ii"2*oŽtybEmY-oX LZbUw~K#(Z-hҶ笜EmSw_9safSazkWL" kM/`鍐[ H(cFӚDS7n+_:3B"\2NA$F4{Sи:@Wq&Nױˮq^ VAe~bo=Pj7W7}*І80Q#΍LXwgd͠E(^gD ɌԈ-RLM fNL~yijxmN:.ܡ]6rF XW(9^VY8{~8)F؋q"@.Hp]wP71 VWp]}Z<$S Ɏ-;9=a+(0es:Pc i,^>W:4=0U2LtvJTz^%i0'2 _kz``4L7cMm.[PP;ӥgIlH=5pGfʍH2ɺyZ-p N}?gDZ,̥V_Cxuħ;vrT _"Z?3O*dnbq^^B(Ӭw6+$ZۤF151(:u1h["=CX Ps-}a‘m;" 7y765qűnT(i^1JMˎcf:/_흵4-7a }o:,_g_P~洈`4E\4v*JL̦q9~Ό1H:e|Ek' K(6 h7$2nO cS *xvZwI6Wѱ<2[fl4=6&9rP!YU FlRY6oO(ز=nk+uTvXlj|DmsyZ3*|[Թ^լV%`[Y1<-""ͭd߁?Ѹxs3]R%b^cB swEFiy!ꞆU[NX/@²}-Vޣ8+/27TWdIN[oIo[l%1 Eo ҹs=WrΠM4YH4:\/*Ǜ[EH#T8zCotmJL2^(p@11ЬtZi%%w1:uqYHўCv$uKƲTGurӫgz{ƴ,~-g+Cj/t L3^F[ 7+pCn~8L/xDxhȋK?8 m( `oWק3#eZ:?r W5L}ac|\`_ O{Q*8WSEm~ _(N*٘N%8ki6Xyj:2Fv\OY4/܄^fQ/t,ۊv[)u.0gK4|o\R= 4! Vt OPFȢ@81jXż/9!Q55Ozc"C8'1κʼ]>'15ƶȻJsZe)|~$l:E!Ub PLLۈ^lr8jYJ <~{3Z_W[..a$WF)k\URRŲ8!4lcl&tH7<# ?=Q*LQaջOPXOvgoOUi!rDlE'~ÔmR[u`=N#IAĉ dY`nx(id j.2Df2OW}m3 1 {3[q֍=22^I줊-٬ƋaTHbxN Nae=#n˞Q Ny2"4*=0"$-D/ד,.i@0 ̞7D&4+"([&oۙ\>@\(US)fra^ N *J VD]3ܬM * gl.[sfN AQ:ZW:'+ٷ-þZ Qj>jC\ugMirv~Fx\@h _l[ - n^:zKD$3j=YKMx( `I=3v"^45r*w(R.鶜nxí!E92۬&brR(Gi?MnO l&EWEcq^TwMk)Y|x\хK<8N1(ii T/+3Q+uNV rz; lru+~rjDŽʐyTKYeN^}yXs#9vDG?R#762N]n$fo]B/HE-Ca/g1/0z?`fG[?*z&* ּіewenfL7MĺĤQAXEp_7 ]#ܭcW/KoL ddl bBKjd[M_gl!6䪆ꚃ*K5gD]rìpz/|-ʠ\ of7AByLX\jY#DnXwWF^hrEL͇M7nwXw\=~ *{- qq}ѺPn~Y|pK~ajgX5dk(Q( nZ!ԍBs1:[ՑĂp <>J9}!\ƃO}+)eKǦ(Aa? %֫ք9ϰ& 쵲D۬ӭڜ(ڳ3w66U@Fʍ(L b󲱾k wACKݼ?OUQ3'2 ￾?7-1eLEV:>E8ʻuE jD2<"kakO^B" Ph$l,F#,?& bTsSm2˽ڶ/,N s廛:GxYX 19>Cv\ϲ)ܸp.rR6#V "r]K'm/VZTٖuE*Qu'D1⋊-m{Zt8u3fa96oWz*`j y42h4@4 (0ubX3J-$>/?**4HbhAh9{>Yqmt;ip`T Vv#ze_qѷ+m]~mS5Ruw1N ^zFt,̸J߲-4tN`䗗;g\5l#waoD .o2j.rU N"xܦ */k-&;k w}o #\$mV7zn~eDNNOdJ L|kθ4;+g')vKյM=LEDZ ިWIz'L +x'v%hYǥ0̋i8ƛQ'>".UK``AaYVEZw-}g~Xa%Ӭ~4c1a!H☦D.Fo@IY ͨNtw9x[ TN} 1Iln~WX %]sPyik<̣G2)s QOGWsx;G[O|c+Ge oX(#_<@&%2xgsqqM1[aEy)MjS#s^+&%3E5/i ^x߶X-rXpz|}m -]l0wNY[FԴ8iYKy3 uV+WhNLJ[d)%AsjgJH\.lz[W_Y.͗_X!vrb})PZ^qC))SL06Šw^k/yds#CeF3eDR캞iK5'dFnjyM&ɶe;rtzEebDlQ8}9'j3Sm3khʑ#W ek-mML Y`&EmԈ-SᖺFRGeŦƇMtKȡ6>,1+˷UNUzXZ -^`Q" (Z/"*R)J|><%KXr( Xa<&0g}_40li8yAO I)^_zD$,+'b=CMR{1(sCUqEdTJxuq+Q³6V3mt%ۛye_}kI=yjӸ‰NJ 54-!MRp},ωK6{i'p'+j|A 'nrYef긭]aIzܺMlᕳ%ZQF%e&IV a幁36et~\!X}Ͻ U0.{t.aİMO ^<rJϩgxwf#7a }eFuF6Rsrc(A{ Nza߁e1tx1+k Ӷs߽ٜbxe5_Ztqamľ\NjEenhf I+>T_ 4(ޣx%W8!Vtڙ9 +nŞJNT>15^xH~X/*:) $PBȨA O$"|t\\34sMq<<秥/6N(V!4=#(κ^`d&(g:D5|7Ү2LcG/k!P@LL04+CZD !}L&Ng]AaRC- G)xݒl4Q<8\#ޞ1-k ( $Pk 3]Sfvx$v^̌됌bnZh2C3e|uQ9Y! Q#C8И#!/yё4X,qQB@N V|b#ɤb%%u8Ƿ0BoUNHe29cC˻ʈↄ"llfrt=s-YWNL d"E(M: mCv7X6"1#SsQ!&:bV0+S&cc9 ^(E;6cY&W@pdzgXYjo7*ؔ8欰jʹfM+{`9zxNFԏ _V׵cA  _L-xˣTΚI!\69g#Q ]hu}O7tc2q}L?$:ђ6>*XH0<2Cc&$tNۜfeD,hIyzh4݃pnᬻzH o׾l1If9DarsFmNG#RkPLK |Po;e˳VH!YfAC uus!H3#[l0,k$xy`O'p{!ЛJ`LHjϯ'c#mM-j_/<?iק%;l#y~S?B 3@~ 9U{0ĴyQp_FW/3 J6SIF8ξe /V=㡌8דD| 7YA㋦-bhݖlʄdz3 8d 0[&P_VUpep՝oK(mȆsNGA@)sMH>}Su _xuD ]ΪI%Oomq񎲍$śZ'ǖTq=}FMH־l~e1oGGKyN|Fh gAMǘЇF;-NEtI2/p 8tnEF-Vbn I()8DDEH|FX!h6&76'+NVR;Ϯ} UeՖK ŕQ3WԪTŅ|l&2N&  > OkA~ "pm4["npjd7WSV(ΦʓeT,sLK ___eONôjkGgfp۴PP`|}fEG IDAT~jZDF,X߁v^\'kfÓ6fsljH*:>zI"IWD)#Dh` (AY<N" &Pf_.@WY<Ҩu)'4nTACO&GF!"#w= 4>4Q!)o)0H)ugJ樸Xn}\a""H"mGpwĶXY"HŲ^80VJŬ)!CCXS}CZE /0eTVX燓x:%E WDK^+QThY->hi}9 vgIQ*(4,4bu+l^4;k?x٠O6qbbr$h/X3hYmLel__e@[k}LBnVuhǪ,o W;bK6kb{X2<1;S,S!)e?"YS?., fR([DF)m`bfjJgE.VЍ 5!,:ˏtW7ò< M`RĐY8 FǢoxi%)ܥ1ۇ5p3(CPˁ(/(-T3m܌7bwƄp'd&%{_g<45 aiY,*Z䲦/i+JNeZ^leoX4-LJG-rbGG5/gW(nwۖa_-(G}5!q.ẳ살]9LA;?#tq L46QB- hqjaa݅tH7/Y%Gu5򞬥~Kmw0ä|/syi;XZ)t[Nq<]msm\dll19E)#s4HZϟSU]B7k秅jl8}dfvu肔S ]it_>f.k€%W]ce4rXΕ(]B:'kzrIhY69R?95EcBZeH~2'/{uttY`;"ǣ_.7 7.ơ0VݳU?xq 03J#ܟ im=lAk^@7[m\Vq^aN\ffk`ɴYzӤHKL Xuy 0mIean]ŀϕz]jw~cj= #dSeZR'j:=cSF W5TT~_Y=#jf| nWbx3kU7,X_ #%gR6.'8g-!F u˽ź2Eku+fj>lwJcOKV n)^wChKxօޯp맾K[u՟؎ U%<ê![GyBIup\w:pn"|`| iJDe'dkNAWȹ 0|kD\I)[?6=D -NSNq<|v`-i^u&Ax5a__e%*ގfoGnU|DўEިJ<6Tn\FAfBTä]XnǸZyڏq ?Y, goyQ,n-c*Bе@l(EWޭ,JT# p @ qD!        X{\{X DC         %`Oc9:^}=/5uaD}JiS#'c.t3~ߞL{o^;gmcwm-e '[v|3#ꁯ|nkn<yo\})#]zJ/$x.52&@`4=m!پSxo*+4l=Ҩ;5/z}';~\㶣a-3+>oҏ䳛b|uD{io{i/>M[v7=ǝ S:##'>ŒwYr͜l}jz\ò?㶻~C/=nJBJ@IӡaiB*ÿkqϹRb>w0;~yWqo Q@@AuV7{W!(`sro>p{_^~O*}wߤC~T|n}~~; e_{{C^{[wO:+pI$PdDWS,'_ڹz@2;xIFpݺlۺ}@-[ӵ=B Rʣ^u/)`_ӅV/{o[r8}ھWvl{/p|Ӿ+Qɗ3n vK#|L5֞ .  嵧~xzqa?}63ꮇϫ'GnopO_?LY9;.Zڤ`ϿrfQ_;kUEo.r"|wăo ڵ?|/֏GVqM %m͈VLF_l8_1^bsm ]yV\]Q=j"4lO: ǬT3j)'FQUPA|şӿ(Hp_v#\!('[Z4ǛG[g\m@м;벓"t3¶w& ؞mJB~]|Xӑ__-p XzКd x$bv0_̘D   gWHX/c)׍%S9.6R-aCy,F`tc+uWA &ߠO=7xDqBo㴼t\j2]:x7Tw$0VR"CeF3e?vݕR^߬M)7'cYkc]RC;<vNeN?}o _o?<8F6ҫHlq<\Xw\$AU;Ki?7g=ʟ(~w7^:(l=ү{7SyWKt0Z_z(~oWj>6>xi) KFd;N^îIaW[lGgw~x߾}_-w縐 {1bԱ~ОjꎟDNKuv:bB'EؖEPsR*.9w?y0aLV%i8[=<_jEJ?VM;x_XqxWh/= …DzB*5<䒘;յu ;^s{>6:~"fqqmo?SS}`8xnbaiqNUnsjR-Ac BŢAty<|}e*>`%N?-* B[V/)*_!31e& < ,?{~FFRdMP$BdpKVC{vqˢe}wOȨ{> t.QCSP{>F5?}2q^o=O$? 9t2i=vt)+v]6QZR7_msQu--5wLO ,LzFlߑx|#CVaQ&i3q,sOW}GWʌ!3޺*!%:pU.cŶ׃vPn{ݶC/p:9>;wx%=Gm_}g'_yQC_ۆS*Omhg_~OA<(E3lf͙ %lˀ㒌bȌaAl7 ςPw\ݷ_ksV :E}]KDb?w?'vMV6cw|1{xW?A=#Hȗݜ/k y  lKUj^Ȉ"sl>Fhu}O_RĪ2e3ʕ|AAlakYy2 ΅S]oª]TT4dNBccH12>b񄙔χ P{/49i_}4(衡IRiS&+W$ {G>X>2VfmCx01${:\Ғe7\_IJ zg?=W}A麷^{ K+ŧ}E}n6bz i[pa:yN y,'L_O8a2(Ll|eɀbAnogF~67iz̉E%4|)5v,{b/ >UZi xZ-`m+vⲾ|ߑ8zT7P2Z9vÆOriHY9} ho. \uQc%ЇffꖇBdzAW| 9,>P YTffDApQ&zFD*ض.Lߥw~ln` d2%7oMFk>=+3P"@@@`-"ʓ~G4S?}g$˔xSݫkRgQLvtCܗY+w%1 ;/=;#S{b>yN(erudPW?!w=1_7iĻxR5~ܝALfplCxMF8p1bW^S.]>u^UtםQ'W>Ozܙa'}sV|ɍAM<=`-[tX7ޱ7%pI> jN3>s ȍ[}:\ ??~qD.ɔu&@} j`ƙۛdG޳i({A`}:rZ;/ DF`~T>CœFOuOGk';ν7\3>s`OB+rɟ?m|y l OB /6 XhfЀ>{9W8뷿ƁϾ7podƐɷlF4=M~7q9E+~ ?LVLԾ;.}>|3~Pozm-5O=w':돿"̲={a ځ|Ȗ$  XӑVvwrִĄp_>';~\ڎ,WDu=5j=^X4wLJ_ @5 ;Tfd*l:Ͻ $ p%5=c[:vF&iڗi$zK&K4qՎPD`{f>ϞL3]?Mn{""7H?RI$'љ@@@@@@@@@n!o7% @@@@@@@@@@P IDAT7D(*!vB <} DgZ=_hCE/c"vKvOFIz} o_Fm_9@'iYy SM_-M_)O4xx:Pǿ7tUʘhē4\F=OioLiN|IA H /F8?>w-f3JQϭ?M~0ߞͻy@xS_..į9zXꋅ=;PNi\WL,2'TݵdqQ>7EOdr<J\ҟ< Xqda|M$񣴘Iah)g?11IH RF(owL '"E=_y<" zE\_b~HOXaXAj~_ӽ嚟*kN3hrz7Oϗ}#BW^ 7UL2s@ рHn5Է,ԧJfɾU N+$/X#oV~w EQpw~~u"ZN+ng+q&Q⢜}*#tm<)U0ߪF+F-%1T^(~U}VTf1KDA<L'=RF y@`"5ot_?hC=EW د`JZK[̃+킐|32ޟ^hd[<{ێ@3Ozξ=<`2 Y E\?ʾ6GuɆ^H[ŠKPD D?7h*t΀/t !J>|]޴` H_`VKa\Sm}V>%c:`-`VmH廅@3:= $[Wg'-&e{!A>? Ez='49]g@\)шO_a*s$SyT+́|A/z̒#i'uM*_\ubMцfMϐk ][#LQ_;p YnϿ4~-PЁdh(t!ɓ#>b13݋@8 l|3%H4%l}|$|!,+?$g_>$ 0V%H#ʌdhl\TշWY逢S͗8TwE<E]fȷy`΃?o:?.Q$ְ" ;b r1%Μȧ"kI1cL%~յۙg'͗K\WzL)JGз&5}d?~9O}182[fl4SFCDG=ݒO9ůiJף=_?F!ƱWctrW>w_z/C Ygq<Xvܠ507~) -ʌ[;tgsnNZlBjk+벿x3g#&DQ3s%{Qs!(sQ+>KȆ(Ek.K&%AiBMWỗ52N=‚ ,MprE:[1:. KB8^1uGP/\tX>:Xas_HYO}Þ1Ĉ*e7^2eLAhF;R$CK|sfWG仧33Ubia}̘H&FSN "ڹ_~No,hy`$)ؾHVA'_|A(&5@lڧ}K@n;7gUyq8RKG <'ʘ5X|ٖ}ԼV-â)qzx\O|_\̉AE_zY}jU~wKb-_[#4|y̓6=Ƌ>z\S&KБF߈ O5&.i1fq.vo3}p*Yj ٺ8H}_+VɦE&?Z'XoXy7 f2~.`(z/\,G6.}re <吝!/ GٛݱtHTRLDW҇LGqyXʨ%$?fk)v#덳\o&e[:UZH3}G /O݈ _T-4Qgyf-7([4I  p7y@f ,HI;o ;"A II&7Yf/S~ wkEB$#;Y+}:?_fGf}Q)0@u|hϗ|o|S]E-Xl愪ݝwrDK46DT_V-ߧ+wE3pFg.3>4Smǜ  %k97,Bzü1>ԱY}ӂ=Yk^Z'̋Ue-!1z{?,)Y 8{l7}7~SdfibrUѕK(ʤ C&ef}*zpkjS1a#FޅűW~?Z_6@c_v2o!>RH]<|~O,+ k" M[T~H/2Kɩ-?P#,'/T4pԢRNga?_v$tˊ4*_ l_Ȋ5=p@Rwi0ф@ 7T/+39xt} fhl/Ŕo忧1p(n޹=cș4 8&G }.$R꫅_x>(3o#=]?$F9_0nFMp"py!).q Y(/dIwXi="fxo6ܼsJG,/un~H֗DXVXxEMfb4ù7BAFi}0P&b/ķԯѼ 3e82,]G' "ʻ[?睏IiE1{IgɁ3'r ip#kH=\Pn _KJ0y Z۱}+_bL,1Azhh$3hj8~54hAo YDd2o |$ uG^ڲ\*+WӇN(xYB]گZɲJANS,> z`"˥@6\8j\"˱9^bXTAueL&4;8Zމk=CtM5{Sf>5M=r!̖TcZ_Dbeu>%d 7BҡM2?p:&UP#L'V}>Dxfx4pvY])̀d WXҠz`{b:*sJ, YML4/*eݙGX``.)f{#4aȗjlz,˕ML&<@5~/>"x8YH@D22ʄ%v$Ғʜ RGZiIUMِI⊪lYsm QolZ ԰O^e$4}3s'}zL*Uh6Dz$ r/kK\}QcŘ# J}e_jYLUFKU݊dkϼl= c>+2| +>6_9ǵ?MrD~RWh{O %@2 jEi9Xegڞ!GVWuP dDo?n3LM{oMݧbΑ~SOFK O@ok=Ҭ5"x-BRj/Bv1pQ=Q%?j.ϛos&|ƙQAc=뱷?Ǝz9o| 0Vb'=ƳUtRIs=i*4iGARE4HCD[$~4md ]A(JsY0[%Ue&?R,u>^cÜ`+$%eoԙ[@'}qj|Yg6Dg 24²>* T$qEkxm ތ2'Xۢ b1=U'8(z`$!5z[8-S_1<.Uu]c)lA d~dg}_][,\g&@X^D"/7)34ړF5_@~jOC9˧x˽oN^?@aX4$1 Io>wL񓇠W^|vd(9l?:S)"ksǩy`{j_LrgXS9\|('3@l5J&}j&?cq|$ZL,6GD|6MO[m}X͓ȼybDmbSܞT7 &z ʟ::) M ~c'%!Ⱦ`׽4H ge:xKsЕ?kE4/8toGm˂{nnkhr D"lgVSycZwP~BV݉M5>q3oQ¼G>~(MLqSLyQތ~D[ Bı#z0݋V,\>)w bBK @ @ @ @ @ @ @ @ D(|Dg@p_yt߉D\ǟz!q(qIE,;#MvXʙ)Z~AcsuQ&hGzWw y2YQqAfQk~~UceϤ&‚w_ RZC‡uzm /ʘhyрϹUӘ5|ohƈscy H /Fh/h||y-h[%ѝKW^p@):}n !>yP8 ԥ7͏ Qu٬+K# ɒgڋhz-CV]_'35zadέ8|a3gB*aiQxU S^ f螟̓5l(ma&JV~OT/<%i{<Ǘ6>͕ޭY_NnL.Fm*6[:)}R{V=U}QS;40g;K\gWO7VZv]9 omӽ`-):7e٘4ٓovҊYӶAwo:Q u`< H+D 0 )wp%^P',q@2˔`$14^f|M4ޚn:7J#kFMPqL\<9ACO6n۝h}]N|y]1LʼBgz$N6$d\.#'OΕ.NtPgַaQ] ?br P(mlXaD ț<ZŮ&+a=o. {۽4<<.z`l)(ܘ2@'Nqk }bdqS0e0e Ѡ5}WonǮ`+$FPv4$aAT`"}p**+ $̓0msr'+WI^VSw-<qҍYb gMJ=?9C686S(" pfY|4WFq<ђ8߯M+^fC$6AH-$F`ZJɢNʱg EFo\×Y\_yɐSb 2MiJH[s!$c 2:&F}b@q`WP)нV`|-(G5W*DR*'o^)Qb>4僷 44ZwD_k`-5n !i'v:;ji KjK$"÷fRKpIu35\DSS0&̝"5T $Qq[_*u/*>]vԓ*ve$7d'2)ϫG`T· qC 6"3ҊbIf5K#B# y'v(6.U>pvӚ[/RfXo.VZSwͰ8DmE-J. ozP&P;|~ 8(\!ϭ3qŦ:Bw\h[JV$<:]>f-7a }w:,_/8ߠ9M,@^). u}jH|>ՄjrD_S]-2M aue[=47(hlnt཰)GPڀ^қ۸^ɥ-[swB%|PlшO7>[PF˯)qZᗶٻkyg`%)vJʲEd x%hn]}w6 wf` -_;yֺCEEa6 #OM ↃGJ +-\Q#P3(MuwE`fi=N:s~mFhM+p%WԜ&no+@ƍS7[ Ϸr[RuP$=: ؙB&j SR/^6*Kv]\uX%ͻRϟS',+O +)|ڕw;=i{۪ۛ "?+4x#ET %o{ M7w=}Vw [Y%+ o"AbˌP*0DjJR3S5+t: ^FޣOkɥ]p‡Ngrm\vO?x'._\N/-1::0$7b@`@ ic#{ڙ^;sc\@C4(̉wIIlZ),N}eoȕUo恒_\2\.A>0/rݸ9= VIm!1 ɫ bR(RȚ]s:'{q<KJ%õfZ헻3+RV YX;[>JJ2+.Y*XSL߅@\dШ\]6A>&tF}ﳑgzǟ;;DX6R]Z6+\֕;[\PH +'>( ~\J0599M@cxOzD! #zvCCA-m:hd7|&8rqF91*q b"Qɺr? ߚ=\Y?4#(dƇF|t^ pUf Vy.ɢ uxUrYz4@lnvT^!S/{ HVYϪL! ZYфh:H)B#ewXv.eWbֿU'Z\kgL $̵kfj *Ц^BX6"1Jkd\ȭiAǜ|&c(9 !҄JȾ>NK'ac1秥V3 swlw0g`Ki+猍 .8'$oěGP2!uRЯ_eZtZ9?Xcb6yKWZ xg<4 pƓ)7zI2ep*_p7,zOv#Aㆨ` RkqȜ `L-P̯޻KFUw]atA&rʿEU “aԖ 5Mڥ(@MtT(.ࣞeE@ܒb8 0h$crx3x/#Da4(t!YH yym ~HtI='nkaKī3+EfTsz58wh4tFtOv gcEbxGew<=̿†kknu0VIrD$׬&0/* 9w3uÝv~QY&å[&;{_A>g|/Ls-)<^o!%JA^sQ3Ec\d4VKw+<:۽& 4^ xCU#?;pm<%=,+M`9F{:*eB r C*p5D3 Z2,×MFSN|\Qǣ ҲlXIP^7˃owIG۽U:ۃO\Fxdpj0*Y[g;dyt3 &,gA速k }htũq"'u˚<6h@֣-;kksS$,2D)L:lY_eLN6|fe2%Bl#^AakrOP g[g_7z5EFalӕMRI|kMY/klƗ ӜP$摟_žR(DUh0dGv <&>]TTӲgMȦ*eTk}H&'b|5us!u㡋 JЛwQSf&Z{E)+񞶆_ؖ وJB{jEˉIefr!ξ`o5E=v7d7ֈ3Hd’koaVXTs!d'`) $ W ovX/7%A`x(i j- "3">J\HeXĬk}LB1 ,t*H̨, g$`5[}fd81;6&i FzIK:3:~9")KXB̥Qq͎tzbO xD5@4<3iY,dhlqMS :=&`" DsZ1rt+L`CWUw[T2T/CRqn4}Eֳ4̒sӳ1}ޚ\Mj%ҩ46?*+23v44fZBar<SVd(qE sn|.,Y&ds(RV[Bٕ"2oJ$4J )dFoNr4  C@,J/e"'d#Ӳ),MgPlqiN 9bڧ 9[zykM$uyK3^}8zzv bxV6efG7cwfp$V \jUh}2X3hlZjt̲,6,YN\3 6I卜677C+-؛hS'3{Hˑ!ZpkX1ojv)/lj~hfƅȜpMp?:s㜷l5Ln^z~v p&7 (cxcti_}ȿGě5JkU1k]Y輌 yZ]mٞդS̎w _M5{O^W>X֊KLssx֑t/Ԗ}`g7Ղ^ =u e6r,2tAYuv~g~Ҥ=8bDcQvtZjڏ5]jBwc Z0?."?t-+WInzY?Ru3U;[3kz{6# _+tFDlQtvkדwiu!l3@y/:[P!ͽgG}g_FS#wj&TF%-<:{ExxLxշ"pն0"us!uVx?"ߏ:ݫlϬKk&pw*qP i ݬ]5e۸Zcu+o@*u)! |`!X3=c*B@^(y^x)ҍ(&      hB @ @ @ @ @ @ @ @ oF%$"         #@@         '`0 z G\RQ&Hcrfd`J?W;xQE W~ghqu} o2['dh[oڊϯj,}MڅQVoH8RZC 9"1X%/00c^QM]U8IZɇfxIȼ.HBhQ~ Quq-b:S5CύMȾO=5u.uFG*9~%.uueirpd1YL{1{ToQZp7Z4Og@kfxȢߝ[qXp~3gB*aiQxU vyP9|I{r| ȋy M33@\ !~J׺~:5Ƨһ5˒ɭU_ɅPm*6[:)}R{V=U^n}řS!&Fnc 򒕞S'{" RUycA^4bWGF7Rn/)5sԠց7Mr46# \.Nk.Ӽ<7k18*S Zitz1. pw?2⤊l,ꁱ%/pc,>|8ŭ5M hė5\ D_$2]5on g?!3  @s'TQYIFdkai;Y Rze[%Oٷ`\xƅK7fmȳ "%d[ @ avnt۔NK(![ܭY_6?kQxI苃$Hm6;DbsXi9BiDi6իʜ,;~GGFoce7o^2䬟mC0e0[%Mcj2'F}cWW80+^nag`>bV#+gz YnjGSb>4僷 44ZwD_k`-5n !%h'v:;jƥ]XRS_"5J/oMbeU쨓X]ؔ&̘"5T $Qq[?_U8/a!H-eUID2bU #s*@biEU$3M؍ b^GkW;,.iV/(iE5} CKԖz^5I\$TzzcsLw et@p^QB[g;!M/:t6xGk,]voW~Om%R'U20{gLc^YTW"IǤMRX5A 2Z[ ]M&G45"ӄVWCZ~pl&;J7ϿM94׎.ؔL.m.3GׂK!2c2")vxlA-hji_ںg~坁;Vfڹ+) /D|uW~ޙ6|kX^; ڀ|7]p;1SY7 JV\}Ssf7OOl^K$pƥfbƂ<)kPƗ/\H,C,qc_C>c5+]^Èahc+e̻g쬒L^[D7a }eFuG l"5jjs)֙К:y\n/#o'.8C3M]6E.;JMG.E'ŗܘg .@'rD4p3Ӑ`@ ic#{ڙ^;sc\@C4(̉wI8JnhmHaq:+#}F*74r 18G|y]m{-\VJj qI^켕] XEِz&mvWA5׻ti3.Ox8 K&kʹ/wgWd V YX;[>JJ2+.Y*XSL߅@\dШ\]6A>&t^Π0Kozs'}bcH9twjٴ IDATsYWFlI^pAQV ٧GTVx.ܞT,rы*elDטt*@C".|F/1tu_=scZ|C2q<"kA _TC _^F8FygHT9fV/:7a`I cƤ@$ٗWâ_ Xxc4osn2T`}wuvzDk NFgxAf`8dN 0{wVӲ:uaP+A$ гj_z hi;|q~@6'@/%I=7aƑӌk'4QшKJ {fִ!|&6^A&3>4SEZ]Ȁ'5+-X E;waNͥPcԥ׭Ӡ9\o|bt3 iNzE"&$.}Ve iʊ&GAL!Rm)#޾NJNu^ZVTZhr'P/wg ae\ 4kTkP$6=>O|>~UZ]#N粘@nM :椭3Cv} p,`&̠VBiuz^:} /̣Cf(--2i}Cel$]eO[a>gl֟WT p9 y~#< ~%*Ӣ:|'[RP:)Xų x<㡌Q6DH.Or(PaY?;h} _7D3m o \Cgjd~]2@3N h5S-R؜NJ& vIm 0x~\ܤ]ꡞK'Or'(>Y9^ -i*/ܸ#yb$⣑8E:L&Bz6AhЅ 7rӹ~@td!1Hcړ :D{N‚WgfWjkr B)0;#bAkp$h\ύ螆ξNJĀ~ cwRpR~m}Bӭ4I^vH(sr$&[[+.<ۈ^P4uh ^{R(I >;jܓ r N'!PCYU˨ݻ˙ Z;]R,-Yp~ٽ 3>nH߈7 P Bsu? 3b(xL7띌j}T^g֤fËUx(jg'_ݚeh|4 cY? mR&@.0W9A4|Z<(-6 DH u<;/VD}]{K=%1AzhhēoeNEGCϤ@h}/p2 ba鍗pA %u~VYCG7 mrfԜ) Nƍ19nq*;Gb}7Ţlj8tݲ&>M==hΚ3Z$ QJ?S';7,GWtz$S lLۈWP~fԩB2}}ۗU aQvp yO&vi{{Kd;y4/CxFy {JTFWiyy؅/,#WGHTVFvQ]SM˞5m +? x<㡌zD,P֗n.Fd77PG;:37C8(ݧM0CyvBl k_\JjaoNV9mVDr{?s`V i _/7=sxQ@Qb8U#T%!s 1& E+`@A#]VeLGoăNg>|Q,C y!%ֱ8ԸWZङneSҨu<)+U eeI" !bxm8Yw5 q'Pʤ7x˄^ _dQ&¦>Aa>5NT̤2E1"g?{£o®ռ),Qw,D-< 0JŐ5`/*l*˰͎?n(2Iϭ,ԪЪdg<4 ֭|eUYl*Y4-.gll:9mo,oVt[7 axNf4X98+ϑ#C>ְb˯ lRv_\&xE 9yᎣ+Bo#du9ouC·kݼ2w(b QBD-^Dml,?X#r5W(qd=&@'Z-g~LZW:/Eg8VW[&6m5#m—}fzS͞+Ӭփkꅅud<-5emAͪ9h`WeOs]#GYd͠ ]vk]]ߙk߸4_QXyԃ]c }Gk75"NfbAetP3WT?̄W}'G]m sa[!a!R7RG9ae#ӽʋfR wL_PYڅ[#K[{ꠡ8VgF/ R'2R  :w=3"ݮ䵟]"G~(݈b<&fO^B"?D(O {6ms wp%e46a1*gFs_\]ɡ}GzW׷&y2YQqAfѨ)Wޤ]UXpn#?`{+@u\3UӘ5|ohƈw$F{XEeQ?"F;|}nAXS0tH݄Ai,\SRg4?~WYWW&GLџ%-ϴOFweȪKD|fV/,ݹ57 y3R6HN`s3ˊ2p+jڥϺ.V],g3"Xj},l9XPz;/8K5ʂ[\Z! ;sꌱ> /.f'qO6ڻh6abksxjO#s} Γi!'015ʒ VM0KZtplҲ\{*6  h/2&.*Es(fbm^Sf`댍F`Έش왂2ZRAj]r /s5>owHQsxNk!0+N@dEiⱽ}J+7b1ia64xb˖Xk_*;`$Ȳ|q<5d ©bʉNLh"P-ζ_%gϟ`*۬ +[lvn5-&<)'aa|e P/6LR(Ld75;3v4 i{Wb"uMgטHȌ*P8 [!%f{bFytwTx$U;Ar`fy}f(ͯ,%3-;U4שZNWϜtM o=yLƧg(B0{%rUp+&DXMbDzpph23LV%Ǐ쒙y/iw:F']T>qw8QnzAV~}^؜-Bkkd c,L(CU,x_4#]`9w#U,]uq+Ciڳwεm'x4g:u_E 6ޖR ]g+gNAH\v t ^xګNxMYam=z; \ZD]SMu: 5X[HtbJK'~ϩnr%At KH0rEcubhnЦ%Mn쭪՛Lޚf8GXsӝr|Lq^(L'ZztdQ ьI;ݫ@b8 ,l:y{Dk4s8Y=T1U^sTvNS1N bXsQ=P{߇@m'YIqbgeFB VBB4* T>&htAYfæ0Spʊ|4Tw|j ά3醛 la3(d/ Ս<چlA,~詹i32Ѩa  ^]7{=sEyrm9CM|YyLS! ?)E5"̃=Hp23LVw hYB(KȎagPcǵ*W xf^p y,\Q*l/{;\gK5.'rdhh)\s^f`8t +<#~LPw(dDxxB+"ISNdU>gveʃ5244[ׁV55̀~B 1 s#=OV#mUnnRkUoQxѢ(rB>rF1lFr>w^.sLbGM 9+2mS# @lQ,>3F4U|4<,skG Q4I/EF # g|x|i*}-6ζ32)N{Hm!/2)ut{Ɖ^Bw?X>"1c3 "9,&ձb9'搽`S)]KV8g±]8?bv&Q@pxf 7=)%jqfs-<1079pR۠IQn ZSš[`&xMhA ӧŰUvD>NNgB>U%Ś!Rg~‹U 3h9ki hpy \*L}uf$H3"!*չZkq$`WL-P_07*=skQh4XovyTN ;0;Z{EܤMҺsZWd;-wr9eS 8|g7hrx38`/jFq ȳ" d4PBxD b4zn,$z?$:D 7|jY0ãk05: IDAT:L5=LbD-h ]4 2̈nᤝǣ q!ːֳئժ! ^]HojoIyk7lZu~g'?44iL|#Uܡgh)I4)t:@$"@pze3 mǡ>[5T<3-&G{:ҙqp3Oik nhtTv|8i'm^ q:eMllMugt節#d 8r/_QCihg&SS0>&Dsqb<yI?35 GT*S/rBbWϜ>/Fh$Ҕ #Nwlwd\g1\p҃Nj0CeALM꺼eo %YHKz[ .?ؙf9ZAQѤ.dzͰ:lh>MH( 㐠Q`x8Y j-Df2GW 苘Uxȯx6s6a8 OgTWVWGT$=tidx)sLP0#-NaDf:!j9ssb84*=,`fVIڍ'\ En ]ʀaXl7ǗJ3i4vLAYW&2~s}uJuWpG7-Cؼy]HYcИI3KShm-f\aeF|tE+cwK=+ϢyePM[mYp)Ҭ!gp$lL0ڮmt>l~YRBaD$Z\қ6]jpɠ1sf ^~`iGfɍa\aQqbxiHH1T%bT?=5rtD3W3R'WGlg-<`Ө#8 Ϣܲ(}Wfy#x̄(DܔTʺ+p"Pkrو-:+/M3ge{7mDIm ͜mwX4ӧ3+*&FHss܆;n;ت4t+֖;(zN^\,8 [*A\tXKf*`Rp͇1PĐA qBڳ\kgydՇj ʯIԒnh5gUpeFQɵkYd~t;tAi;N-\ts1\~#m|p\5|ceV!.XWbWi`FᄧHS\lh+H+56SH0/L# TE ΥԲ{l}=+eYri3)&َ҅0^ξqi;sFd4GEV{uA_4P^ 4Q"4\`m\7sdn`OH̵Z'JoSEADXA3{ڴ24t1 `BK+sM3ǿ=wEu#+gμfMc_m%A2`ey(!V~8 θeI"dLaҐ]7e %υ|暺/B3e[ VHTNjI@߽OWGU_EWS#fZ$qJxh]Woiu㨫pY!h%o,L砍%FWiMW*|Lٚ1VCU"rJrSVZrq@3e۸O4늴U?ǽbL ~q`!D\1*BĊQ QL @ @ @ @ @ @ "ф@@@@@@@@@C/"':s;Pb2rqr]n?WyQxɥ3Sè&x_sud6?1--5=9KS^yg+n /)47YCWkZ[C@.H׬,8c%_`4`Ԃp&ilB]6)=<0-E^oFtUߜTP ތu}nPM~(q#?FX<7nդijToHw~(-_(11_m',[WŸvϞfIX`3 NvLg<{y@1WdnڌY%d¹z ^r?;D?\J/( InXBh|mR䰙!;\QR}婢K]VX19-^;yc^27~{ ayʗQλNb$h &.˖O5wdx]P`BE,E˞6Y@qBe.\6wؾ\$T⺕t `$1,_s־K7^h<5UI7/6l?s쌛5vj4[Z!t}$~AW|fg oX{3~̹Bb^]}P0f2|H:m+5xɲ k۹7xάZNSߚ9.}`UxܲʝUeKGw v~ZH%٬#kxI;<&FKk:!יF`݅.'=;lƔçV*ю#tcS)RQLrOR5MtRKmŎ> ߱x2gOC0g0[Bhl~5`̓C#tZ?eWےstn\MWõٲ}bb.j;*򒋋sRbxLQ-XLBEΧעu_ kEQ72VqmH$ aGqn ,(yYbFj¥eE-v.sD-ܐX`}ɹ~uXERwP8'G] 41^ksxjO#3 Γi!'015ʒ VM0KZtplҲ\{*6  h/2&.*Es(fbm^Sf`댍F`Έش왂2ZRAj]r /s5>owHQsxNk!0+N@dEiⱽ}J+7b1ia64xb˖Xk_*;`$Ȳ|q<5d ©bʉNLh"P-ζ_%gϟ`*۬ +[lvn5-&<)'aa|e Pȵ 7LR(Ld75;3v4 i{Wb"uMgטHȌ*P8 [!%f{bFytwTx$U;Ar`fy}f(QAtYKQ ֓n|zA(\qr, Wb-_bynRJE$v,; Q(3i\~.>{~bv_aIzE%K pGKd a36gty!XcaGb4<UߟaS@bQF[|]JӞs'l;я+ xE8}C*H*((L:]9+t: VF₵NMFǃM^uƛou kLg&j\ YM B43CTZ8g{Nu/.ŵ sh ]E2,묮kFp6%'-/i7pcoUdҏ4q<2ƚOcsbVFa > :գc'bHf8Hً/{#,,l:y{Dk4s8Y=T1U^sTvNSP+A`HZ Fz"7NLdʌ7߅@hT&1ATE%)}L&x9",l͆MachAYg 77DgBQ^ :%:x, 7 8XrSsأgezQ#64s;p;Tnۺ=z/+O"ws48Q;d'8 ^rRy7Pf񮚚 26_heRٱX}2BC3 jV[_Cc, !K1P:`o rD -u+ A{^gϞѴ LHx/XqeT$i~މ._VyF&W\f:ЪƱOh!f!ZB.VG\(H.α@bwXI,I1!gECݸԶtusD-JŧpƷhv *7Oe.s-r$s I:⥝< y4Ugm'Ԉ=L $նȐjd :=f|gS,iqX[1Ӝs^val3X.E1 k(t8u <3Ll57m93 s '+%z߻ dPI7h)OI kn&G5y'{Fq23LVv,W89)g VfkZJA /Vx8Y Ro,Q!2gp\0]{;<' bTJh6kšsh^1By2/4̡fFDcU[QE;&<; hr6Iε&__P,j=ZK_|ӶNRdN5LxqHp,8${oTQ)ǁ j$@ A6r} 6 ~ :D-$meKƚή`3E7;018 th4d4z3#zvX0cٮ{;SXfJaË IJRgN H.c\($Rsvb݋dcc~er>"E-\u9uJÎ,\wM&X 6٧ߓe93'ޝ/C’ft?$7ЅצE3Lst_x ~м09)EKJ 7 9#yFq@MVvFϣݙ!%b?35 GT*S/rBbWϜ>/Fh$<+AZEuR2t( bHmP-C})BZr@g-<\3rI]iÏfXGGyf6 4&$qH(Pg<,rY "3ˉjqE*d<9~]ӧ3++#*_:{Z29&(A0"3fuM]~ˁvKL91S03+$m"7.^e@0` Vw6Kf4;,h}vw?[:+Q8#VҢƊ+ˊ̽u üE-*T*%̝xVO?W6n|E 2gMK(: ئ +͡Sil^|ƌXUڮf,M1h𤙥)ѶSLr&Hˌ茋E/0V.?{VE˜̡0bpkxS(Y%C 8H*٘Ba]Hv}|&ˆH<͙7m*#!,573Ac2yҭ̞æӹ¢@G33`8+8 K0 ~zj+ggµO¯IϚs>8;c`g4jH+)N³(?,+JkH(^933!À/!7%$C ܟpF#Z\6bʋeSY^t<`b'3;sɯ[{Tky6<`6 ]¥+}'\!Ƽ1Vɛh~8׍ T{N= +pn#@yk3mچۭpFY(r=`bP-l EÂ6A\Au\X]h9ێvf PR#\r oOxzʭ.>3YS?W[I!Xtkzե;=7NCa;-3nYnl>ùE4Sbf4d)|BIs!*;w4!L3%Fy%:Z#wnQser8#;ZU%xxh8[Z8p13\nZ 9hc «haӕ*Sfe&cL%?{Plܔpՠ\lL6 -s"mqo 5}93430}XW D(ŸPrE bd,'>x)BF@qD4!        oFyKhD @ @ @ @ @ @ @ @ @ `G剎@ @ @ @ @ @ @ @ @ pE"ط}j͛]0bKU3R4P]k{&݌(ץ=E/I_ZqpD(|RyҲݸ$52R;ӉrBIˢg*_]iVTEZVO;>?t@|ҿſ jI: سz7?a-|eR΋ڿf+f?n<xӣlz)Jr I7?yK~r8cTo;{\䭜G;.nAFlL{eK{h6ʅ[ύq((DWoGsyb[~uKWo}e1:;b8u o{Zzlܛ_+g=z繁mn{xg-."Z,ll l~쭝ݘ[}2Wwݦk.͸Ƿn.q)?~|RRECuC?%~ɒq1 [뻸[8:GR g 4stЀ>\=>2fn@],B%IEt'Z#`5%mG?@g&pۮ\: 1[uWZG/ux̨_g0 jD+ p(?vl.&C$AL'{#'r{偝߿ܺ p+lhp:lrd)434d]{m:T{`ߐgֱVnGk?r8ѥjo) ~9bطc^5׳˿PМ'a є@@@@EOP;8 29I!'^"Ix:;6Ųan?vm/PQG1Q hLNotlT/S/^tE_dd2CuF#0gDGlPвݹr'X]/<^)cs}9qk=дww wrt+ܼWQK<h\EX:B POit _'~ /#U{ŏuu:y>|oZt%NhOok9U3s@tU"zɊ22t%?iPcʺl{jGܛ֋TyW2drs6¯ٸ*廷6k}a _iS^Yxg *?V;>y/HR $DW4~5tq7H΁WxQsqCR$&Q(dlgEfpY~zÚoznmݛw,\F^c }{z/Ύ^vÃ/WkweIA8'ǾW:cC4OdqN CÿAr#hC @ @ @ p HMW.5 uZ-Dg^b+{{jR+{$5r¿~{mw%=[[S ~s憐]I@µ}3 $06iu0-w=]]6JZ2afZxD |\|8h e&!s 8*`4ܣ={wż:_vMg'q~3HШbta0HxӨP2O:5NcE 40KX!' Tyf wy޵i/Áz ;Jp N;TKZ<1a,$ s@qD!P>WWpW}6R|߀x=L!LL Έ8q6RA\|Ӈ7-0\.4e銗~wm8ʂ䇆F M =&X9 ;d4A6rMg4$*',['\VZ ܼ_?/\.| 0{Zm|8<ȱ\8s8qN+(t\,~cb-)992vN6')):!q]_+[ePH5S{!CDe0aZknWwgF/2ޗqӄ5Rf(K6gR&AOhPH(떾嶺?goJԏjz8 g/Vbu' 5-d| s(hB5vN厷!J-wn5!Xms&525~&: J}(EaI\]Cyp3Ͼ~Jodw>vKem]_ПT|`9]Άo- crK6=~s' w" 1` =:'S|5C; e@J-9/ћD]_WYK?M)^yp&3"1{ކ'|b+p%|UQ4#P֎sX[vݵquOrxk0A|z=09)ZCY[aRx a>hQc>ޑ[_%/|:ƉZW^\=̈́ŠH{kw}l<2m,dksͯs|%Q K[' >ښk;l oVs(N     '4/q;=k;֯cw1aO~&ؖwov[R< A-n'rd3y_}xa5/7dlX1b3@m⌅*SG HӫO1xh_x jOpot)jEU_o_L#§ɺNmzl>5M%ͺDk+(V޻߳ +Sj>xQ/OFz{?#<>}\v_MoW_OxwK#սyS?sqѢ(X_#?u矶"Zo5bn{e/o䗑/CkBW).tZ9,Vs 0(}fǡ@g*XWɕL7~w| @ʫο.LgZ\8WRH.U߼>|篽m_~ &      BC~=~iC[(D1@H|H @]*] ByP .'3+?>N_R[Zh,!Ҧ3 >VyxM|~1Fo>Գ-*Chrq.㢝ݟm÷ߠcV޶//\e|oN{+]8cHSw $[SO ¿,H/-BIˢ/Vg~Q: kU/a,['T IvK`{_}s&qG??cR)>b#Pשcwӽ~TLQ碾p0k)G^1hO΁_tTN\{ o{3o)6wnbn 橻Tri΂63, w;à ,(% >/wWAօݖE=LkVʭGt[G~-ҺPҝ:th>Xnzq|3K=@r]k&3D7uLp Gg5XgW`ZOw4 E\g@Y"q>m(+1TOk3` g0sDdžR5`ڟnI ɷM.n`4b5$u55OE\m #]1|6$X AD& [xB ,d㺐H\/5/G&/=P/ IDAT- \ F#0G|fcq>:;FAn|m%I\C^WIUzڅlEzDl] #A}Ƒp2eg1g[HB{Z y24O}0_REOm~!K-,e6%$m\Qdݷ*qjwm71ع1h foK^,w&#ʦp3Xjݐ4á8ZwqjoCA7)ڲhrCCTxi6[&ƒCBχn0kkl?L\WFopBoTV gq:)8}Rlu$2Һ"ljTxz;zT^s.LHv'cPT\`{r!,l IiS?oo' dtrx"_,7:e~+D jB# A&ؙnl}4G@4! M~_tuz^_F3>rsrX_<?{as4 23؆kIewݺP/Qmm~9d>:Mi3e, JFi|=K*Iϖz`|S;'!c*]}G >oτYw<_;h9ޝ.џ |aU֎s*OQ_lss\uEp(k/~, -Ӕ9S1'c#Ҋ"8 EGV@ڞ&=r~jyrvtncl:l"Dc"7Qtɰk<[Y?Y,m?zH`1x Y7Rfnm``JH/~;r$ȃG9z~둑Ƨb `~*o)ȦB%ƭ!`ԝo ^<cYX?ߏl!ßD4W*E;.;hdv8.'AM׏mOQTK 3j;xؐkIkȪ~޿)+IV5ʈΨF/Nz֘h$Ch>(2ic1ot7τjz1L fH, Q1#fLuQpG*.\lO8g<𫤯S2^ǫsL~5vz9Šdc G68gHKW[Oļ㧳?c vZT{8h- n=H.|ū?Z u18hX:_B@ @ ܡTu?+ ^OܞgI)]g|h挏mlp VQq\$CQPmrQ=c&OV{[GOy uݴ`~B)|o}Xy y%˜nuZ]vd̘hJvXo,Rk}w+A&>ߚ&2vD٢6 ݹ5WSIوh1A i}dû|8;쵼CyKSxgW =:>p+6F߀YI@ \X>0ۥJ 4gX;"ArmDjkR R*ZT%P>)^ w1M(zDc[)ߵJ8ޙ2.6rև}IԜ7VR/+R3O8ʍs{f5qvc TqTPp }6[0nBFM?Nyb):Λ4KR(z Ǧ:O7>圉PWdPN~ŸPT-l64x^H&'"ùNAX#lRDyӍV0!H'O؉?PX;! :4Z9·9 R%*Zt9ߞFjAOퟩr  I90Ag9 ^AHR#}wsfᐬG۶<1zS ~Xl,8N~hhc]CP5Bن#'!n Jv5g?K / I"5d YbY.^sνyDiLSӹ,^!ZZP89yKaSO%1_@.7if$$[U 6+۷>D*RjPYA!ٶ.~:=NR"F.R^f]?]{!1^%}Ř CZ"}h Fp7i%}2 Fjv8,e$ ގcohI7ECci^G HK+ zeTU v;3LǾv7*+ݙ WE`Qi]M-Xg 3L{)e"fM^=L[36 ՘ylN3'T8X:>3gLL̈́dS!DŽ2hФr 4g.l RMy"M7 ͦʜA-H3 #h]dOOABe8A"K9æMԹ'q6y0+Kq熇Uѳl.MB_2D dQAج(7c*˧2N"bJ?RV*%2ET$_~[:)91ǩ$v[~s$ݲW6/!e- oX%[׸ [H.?~@, a,q6j_vJy&5, ˕7+(?vaY%۱z,AF܊*kR6mЖ!slW<j0ڗc / @]xqF ?LUhyp%Vh!H$S'zX4yWV~IA%5/Q/It]#+iBlac]MDZ.r,k<4|Ŏ"KKp][X>dZ6fLS nIG~nI*`+b3IY͆fFPhmN.ͥ?a~lFuM -ȖSHk<Ε`߭I@]ɩ<SL^IMN?rC#'}ca i⨢'lSdk͌I3<^!FH?lVAsv*?{($(tK -י@hPo? e|4Yj*vk#U ݘc_}PԦ/d^t8ǚnM$q^hڈ_GBo'αfJ}"ENGaJ+@kز8>czM F5S}WΧ$3&`ɔ_&ݮ{Tb ۶|ΒwK V]2}كʀ{$lf?>h+(P #%q0-`XWPmMHԚ>eV"Wߙ9/!yԊ _[´[7_Xr9C@*qOsUČu}eU{ނ#%+67J&H@@$`*o>   |, Ncv@@I0   hUD)OZ߼n@

ekVj\k܌m_JwoW޼zD)7xmisҖ9sR%sy͟Ge!N{'u [S@]*sV"?[EXmBڡ7U85*SJȌʲ=fWN~yr:'8^kq D@Q}=݋IP6²6x,qdj^/OkCw^הS!ZNY}KV/.5|gCP:[bҝΗ.ϑrzAdmWƥ,gK3 p& Ϛ csiJfTsGV:pTo;|5VYFe| w1PanNjx" ;q&00 VNI%_Tffol ֭*(5*T, oɅ-rA:s*B/5GiQAuZ5#Y3!^BNaؤHJ|g;>r+I'Y:C'4L%b@@4y2O^\)u͕k@W*vieQن+*tt:Tb P^^櫴Zg= a"uC+#gV^00-)fP/>neey<<Xv #5Jj!%E!!ؐZ `bSyE@9CjݸMR sbڅM^ Od"Ipi3a C#MfrdLZDE{5˛:dijiWR1Rvž6^qk!@C8IY#L]K΂ G*F*5Bw"$dMO'SuW 摤r=MƖC ) qO-9U1 D rXKJU}2S(u7d> r!tw粐L6 T2=jdrIˊeC-:raFZF%|~K|@OfmVwf>#j)u(OLb~C@ 2v) [qwmo$ƨ>Z 9q6umxu8#R=0dIyimm꘩N4NF! $OFt IDAT0DHɚ3G eQ`"4Gzꢡne5UfLħP)yqxi&)l~TH蒘*)C.'ZۆbE)/'ںfN+,y\*<"uH0MTj , AEqm<87-NPqy& {*|ܲb0%֒)/Svw@Jw]Hެ4w~nPIWT Bx PyMmmek$U"vB[sWWDcԬĠ/AjDjg5)Ww)4Ly}5rRpB$ p8\+ºfIj[U<[˱KIv pxqLWDJ ֯EH\>\h/3%aEedfm~=ifl81lc*rFZln}^)O6/bޕ7 ͘.Li52T4 d{v5K +[HdTYPn O/6$JIA")bo%?AzȥKsTU"!nSY8XWk9r>k6ŹajBN u¦aJqxִ#ׄ ( >`ϻkq caZ{{k-/*>Ђlv]o}իhrfVrzÊ9dlE2x/m|ODbs`a&ҌG, xߒ]o,!J͡9HX>㈚Y#TCd9ttkx_2:W7 42 f+VK-9G2Tp g)fse ]^$}U(9sEɨܟdr3j2|j)~ibiL9,~mA -lv9(>P"3y^U5PJpE*Q+֖=aBU'` 9,Dٰ̯GUn)kM,)u9exIbW(:V=,4,. \ƦaJn#j@ɻ/VU2}#*S*/v^<ZS"s'HJZ!̥Bw > 4KFo#{0jئ>@@T3wSuy@@Eo)XGsu$yMrsK_ؾz,JGɀ  YM@*߲8c] 5qr0o|@#   8@@7>S@@4C6\+[.Ö**J:acՊRLٻw7>oaЌK6 {ωc{T_cK{򹮐Q44Ȳx5>ZW :LLg~k,>[̸{M{vtWl+@gڽ⟦*&.9=uoxRN~(ǬE^Y12K; Ev.K:W@p?Fj6} 3{cI|A߇WCSGYpVg?'b۪~/@Fϔw:vڠ~\V: Mgϒ''p̹G8dq쪅.:sYr*fkSh@}k|FXZ]~yx.FM6ZF@!Y#bRan#7)[! ^/}{JeN5d̒MqeF8f)  U ,߾$ r4\iؠߌ9(.)Hvf2:v#gLS*%1siם^}njybl<1z# m,Awwg˲$u3Osψd!tnDM,K3}yeɁO'k$+X;iً})okљ5pݕP`-p{Zccj>yC~\PVZBM" :uɹۿ/:- f :#JsΊ?Q;?/WݝiaHGT$w򡼻|*m Q߯=9VQ6 ˘tm"ߴ +̽?--ѓ}9#VH"~CW#)o{=eIަ-LZ9 jeg,lԨ2Ni=sx jBW_P{ZgNo#^70lESH@FR֖*,,nD||t&깬6 *ӹ]nQg ͙&GU|: h6PнEq-t-џϐz!>@֬/ke#}gNSvx!a~ͤfa+FC;|kۨd![v.]3eb_c43{*c6VKWGk7xX3W|76ҽWV$>̟MlOk3sǮYmϬ5r̢m;iC;|&mꑶ֘l>iq^05D0k۲n³ 8?,"#29ِY3oφɒ̘~#'uݿx_֪T/ߐN%1+Ç~+2gUO]iGh>|># qJB̤HI=g( G(t6Ұ!WOg#vXl5F)RH 6Lߺ"Pra͸ 6F-BJ'j»u/(.-(ػnl G0O%♛ %IBU1K 8Y#wl@驕kZij2wN轗 .YGQ= d9vIG o̶A|6l4IJ6,Tqi;y\~ig `kz#|]"d$\FDÅb߳<Н>t!8®GУWEtrڭSy"~g{.`MܝJP/{E>F'rn5Sϼ(soݓ@0B|@m$>_jNɴNv99>ه"7_!rX[wz# GޗmWS]"P#W%ɏsO ^J ,ꢓJÔm#G3^w+'_T]q=u#4p4hf#+F`4jz`ϊy%-և@ 0, GAR?Rړ#(^f&sAlAF 2]P)tYrS޹M>H{(2DV"A3Nq zA8헙_EKm?}^__cGYGHeHdD7K==6\/|ǮO^FkN3RC ddi/_j0'ּڔ[-ƶԁ!xximL5ieK򴸸>U^zo%2ڵ%ܾP$kې.6^9NZ[i1#ھuO53iBYHJ)hFk}XaKSB$x-hҨ"O6()dGP&›,gZ\[,8'?j Qonv_媝prros5$x p T>u߈}HJ0I3IՉ^߻qiiC)Ѵ|xA}QJ&@ЩJ"BMt%75yeQEL!rP9c7ֲq/ `.! \b τVr  i*Dc^m8+EtZ'Xbg,IY;bȹ<~@'pHCZ5٩LO+Z7i„ ;Ɵ\M49aRŖEqU͓}KO3RvÉ>/.   &Q  D@j[.JqR 3"9?Kyt#)s{;t {ʗf%_NGh0jSLٻw7>o ld{N۳5";_$M u.EC#,W#x?}T/g%B_ qfZ\lMfz`l˚lзS` l5enk`:⍑}e\ޱN(sQ_rE{-0RKf)UZGd\}TR?<r(XY\6:{3yz!䳑*%1siם^}njybl<1z#9$Y&Ζe=I<'fɮC܈~YTf܏ݳʺS-`Ԧf/ T}Ҋ(UsGmjdе=FB\aLӝ7={~v䲌?oOs 7BupZ1K3Fuq}R `g%ܱSu΅~}tuO'F$?"b)f-Vo`) i0P@tثmh I]HºLĭGg˺kȠ2E&~uP]NМYmk֩ "F+ۢ8_uΖc]wOz{֛sz!>@֬_N&3');hXYP?FRg>s5mTstې-VtĮ2rU11b+{*UVKWG+?(fnlb{>J4}:?b۩؋qǶ Z|.H<.st/{Y{;"xuգABA6q̝n>|f?lKh%giFDdÒHv嗦y6y?>%AIF;ʵXz`DD[Q܏?\H)_]={}IKW Ci.)zn=x]D'qQoz95'v۱'f_ޤIo'^3jd{"&AY#Xq I?̋R>7= -ddTq@DS^eO.kGZLL9\' z`ϊy%t2zU+REI%yT8zܵm;46B ?N+9tHB&*U$8h&~ ^6za+)neuO@AKm?}^__cG(Uek֑FAD?RRoM/ |C1A׷Qa:L}p|&FY31Y4FI}5of6JCK-;u`yZG/r}{lwyZ\[NpΪi^ŷornF\we(RS[HC,?_W>m8+ffii{nxElHclح?Oi^-ZX^с򲳤fljӈmf*J]@H@ ]1 ]*x=OvvaVzdnxɇS2,8.,xӁQ}O? qf({aAʘv]Ⱥ \~KBzO[$4HM+ȇ80Yw"$dMOZZ}S3WUT,˔Nr.W_3va)]{8}aH+ǥw 7Gr-<FXW^1X,xz6#xILPok`9&ERoA,1rE1$syVR)2/Io)iBlNVx7j(2ΧS)s)]P$RFpK^WF e |T"{֭>s1P cjơ3;;6fu穋a08g[Pjm&fb倫ºi_vѤ06: B=L [3FrkE-K2!mH3֥~^^כi2>ryV,ޫکΜ˶3vwúIYgN?ֻ NyRwp3N>dQfŽz2ll3g0#75"UJlid^D;sKGsI8𮞼T|ұ_8X\N`\a3~]4+6jLϦ^hϞoo`ط`pB[*=7|&@L!  4I^#&AݖG]_^kM|qQ}}3O5f(ՍΏu$ }<~6W‹K'LdD[$;5n͜cĶh?X5£cTy}bڌY<:q]Y͡X>dZ6fLS ]9ڋ-)~~&8w)wmGv=J/3?l1V 3R|\knV"U?ݳp^eYswFPE /֢O5;'e~:?eNzmTO EqU͓}KO3RvÉmꅶ:+R0lOtDkn~lEdD6q#5aLPRì6"|AP3( 4nA., 0 Fk AWNLs3}tS< ۗH4uh`KgaעQy<* ! hV?I` &iʷsbpN;Q[ycW~h㦏*MI{fn#F39p@S&r> Vx*_smL  IT$|@@@XvX   @F   ͞˪m(nHul7`̈>܂7c.BgGyzv ]Ѝ-ҷp7F'zN+_|-j;R0jSLٻw7>o ld{N۳5";_$M u.EC#,W#x}T/g%B_ qfZ\lMfz`d~ܶ5nI֋Wb\^;85fr\IӢa6]KUB*,Ms2x$XKW*K?>s 4!W彺wCS47 KNr6}^~ 3 P)Gd|u? ĎGؾo`|Io?0tWw ̛{OZ_iaH}߂Zo6Pxz衣Vnǜ\v9ݘR`vcg6bOGo:]ayv5EI#r`j2ҌGPwLy;4`=~}"vM,k}06Iw 5tsL0{f2.iX'Uع(B/^Flل' 3t1o'm}O}^UֺO5ggYۊmϔw:vڠ~\V:jD(MyQӿw@g1Ǯ'g#|b ﳪ@gH;FQhϜ/$ ļw],u8v?M28NbnΆ}oLjh/JIZP[2= 㐉U 9 \:u 1TL845֦bр324=\lL*5BF|5OZ8#nxUU+ٖ;Yi2fĦoDT\#{r D̀2D{zҥ/>*4ʨf%OgOg8J1gtD%LFN~?i>zG ܞW<ϣB*oQ*FqY&:AĮ̀Sol&P"*5e5chzEͻ}E{jil0]{}3.۵ [Zpx&jʝ q ݹX:1mLp[+ ?i ȿ7VE@ԘmeJI\Zux5`Z!Ot?6zC/wФ''D3"uѯsS;Ҍ{v|^Ip'k$+Ԧf/ T}rLMyp;Ԥnn98vѧ_?oϗ ]K0a F V߭g1uHQ ~JxvGOQ;bc|~ Wu#C*s;P]>|6tBK͋מW:6 QhrN iBe?پd& }VSa5m 2s0<"$=ϟ{Q$jTuAf b.(}3oug%ܱSߍ A^g/ώ\miz ~W *lESH@FR@dw7g"~t&깬6 *ӹ]nQg ͙& |f Ri2ͽ-%]l9ץkq4'հ4<'׋ f}|YC/;#pBË Enh&5 [1~3\lF5G' ٲkEwI)#_#z-#[W*^:8Zâ⻱"ids%9$|cYp3Zx$*ٰ$Rťq)wr86ŏ~ULෙY>IYydemn\5}Bq$^j*f<&:8nyԂڂ& |^-ZXx({gJOWU![yZAχ, #gsO>8q`ɧ%pa߷ {z9mߌ3Fi?Vie쁬л08͵Ji/!zFAiOj#ѤMI4Yӫ'<ZT=$%Uv2*P]7o#7 }DQjC<3=/ױ۱ǻ_uv7NI$f$Xs#EiDxx H$⾈{q M(۶LtU^W~UAfڵ_gϾ*̷By< fsؐlnlrCb8+RZz*dq]V T$ޛDUq5|nNGlp!o : V*}F ~ҀK3%7}lS9~)>}FbOXca2?K*#%†Lޝ4cm,OAm1#8=-v oY'b 8)Z0&|I7?Bu0L'_+]0鮭H9pDh-uNN.䕯SHH~%2\J嚏T-BPh*?ϟY,"@\x.p3?꽯}g3{QCC#xmï{^V 01hoAm7y$Ho w]^ _ ׾|;wUQ! M&$3Q|EBL.gcád96nb1-*bV^94'tv8S̴H4✜k8CPb}q}:3 -7 w,_G7bIɱmTqS_,xrrl:}PDx{Y%qOfTyēH;Ĺ7:yŏ ̧3ux?ԮlCS^NScHZhy; BC[F;^_P_ε d4@t#P^yExJ$b%} (s"h$$0βP̯8/3#/./gYqg .߸6+ !k8QKՙ&KDz;P$&[$*[ +*S] +8x$pI"ЉXp%zOQ3B"nX<;̨#R]$ (|$}9k/}Rm^ ]͗K畼{_P]}6U޼ϢߝP .Dqrj_o3K6Ӎ'j2U֟~T&3-9o}6K=n[_|!g}h*\.-(l~;_lp?JڳIq*+]rۿ}.eG>:l?S_kx]oDކ2q\jR-֜RT}ۯJpmǍB-ⶹ4%ͺ,77;Mq'}x: j~۟>iׯs[txO~"cߺ7b`߀[~n~_o^~ϰkV?w~&|m]ۿ([?/rp{W?لcUW?GO<`C.\~ޕJV^O~+?4_~j" +Ͼ~.}bXY,6wvٍ[^:Rvz?6ujmJ>q~_:z枿?|ko}"`5w/=$lO;Q~׿g?-[K%q84=g_vFanԾ+ YtP޹CoxW/oɕ9^?-~~Tf?d<0GL &9Jpa\Ho@!/;Ͽ,AkXD(NޯsB7DaDTTO>=tVa E _]O=hCP;?jHq7qI(| BP}i]+oֱ'@W)~[^IS"3 ~+k~JU%zߪ}z)|koT׳t OfSg_FO  jy'`0ow<`e=;l޸>D@7 FxRhS7ۋz`.xLگ]e O&՟^?!A)>ůjy޾hb*(B$![?^B6@BS#<.bd!FㆊH @ @ @ @ @ @ @ @ p+O|(|9OvVqy4'#EvX c(U|4<ХлRێ~wfJi9s,W;t ;[rTkyuH,es~;\?-\:{ΖYz#@|CMye,:H@A쒓MntqD6(ɼ#S>d2J)Spx8 ZN|'Dñ7ireE,Ą,5/[\;d F좣}= hĖ5|MT(0 Y^]+9_VHQjQL 9s&8#6?_ȴΔDxJ@_ԟ*1tuDLm!AH''7B[l[U*3"7:?{^(re5V FclT% BjDf`C#{w#J c>q0jWdc[Pjի/o(Mun̍Mi[gM|cʄK/:X(2|MfKJ.o榥PTSe2x!A<{S5ɪ(r}Tn)"sn,!@Y"af*k+e7llϜ1X$Ӵ9=44  6֯Q[Q T…÷H^T MXv"n,8(>׭51ŦB]t6bǩyjK{=SDGxI+l-`lՉEcm`s2Kkp6d*&*X PKctR2 ᵎ|yam7x,~Z,n;]+ %sh ]E[gۋJiߚ3p]k(NElMoslz4 \ )K??9flLm?`vW>-S h]7]+ _TXQ A<(ͽ1Q"!yR X[3#^tzxV",db4A;nvzF1'c4 1PŲ~u5*3ˉ wZ 7K.!sh>1ܖc%-::VH2 "xDBi|]d"/oiuussTRr&1͘vBQ1o"QZy'FWTU'?~wЈM_[hC4붜t.α@k=4R QNk&Br\_|bKƲrIj$I2br34hkKRףh!I&mƬ)#޶NJN+^͖Y }-lrz)w^42F>ԛP0 '>p{h`و.sR@lM :愭3Bv}\.AlhuP:} k ^_\S!bwmڢ6A.ʺ@Ğ\pL>H߈5DٍLK]|,gj8IȗSXD_{# QF"&7〓y2>R:A;H Bƒ$Z{Νc 26 ~HtIN%kI_3<<jj + ߩ0#bAL*E ӹ#=-m9߱8{l?USWTsu0W@r-Br )r2'ކ'.ZrmDJYF%yz{8*dB r C*p1D)3g[3,wz͑bT`@>)jx$8<.ٰ,D T^+]Y@p q[wJN!^" .qr${bc3USτ@hmOep6$#v_>Yn]EwWuyi4 IDATYO7q6@ 5~K1ȋN߱8+1ζ瀧ł׉8t=*>M550bˑjQ bnPM(ŧ^/ᓓCV U6I)Юe&&S<6 ]/N uLI<;8};ثlkdVKMm9ݝ9|l&)'u(թ!~#83@P4:.|$\gY>Id6Vmk4XŲ?㡌zDP(; ɣ ]0z3%8eA)9kNzɶv徸66P}vnʔ?fggѦq o ݃jH?zw|DVg)6sSm*|:d SSHKf~L(s"h$+ޢ XaȤ{tB>D:VNHjTAp+\j֗#5kun(;3vDs5"yQW[Xuzo)? )Ed!{ kU39+?{#T_<. HJ<|Z\,3P=5T*_܈<+:Oif&G `[CQd8:!²:+4gK4.XYs( iTǚlc' !?^LTPFf&i)V8LW~kצyy5l #H+pCMmPKHɄ_]OXNZZҫ䍅QE"$؛{(n$OfTOVx+#i=S`oZ2X^T7IFzqKߺ8'9"MXyFK]Y9Z"|cqŦUwn">FceU7s S+Nnrg/Pf p! e|TYPǡR2+KӬ0@xfcn-^# *V4>"͕ R(xK=k+liGg֥9sje1i&NxMف^켉QܐcИ~n&SF %(qE S>d)9bIۚ@92< i͍xR}L0/O;`ޖ(JcR(4ёӭ`^D F ;$AJ#}\ˎVdt.?Qzx|{H𤏝/bRt1g={ îe_\s_cx=L;R1u\؋+K3lKX~#撼 D4j3uV2F5j/2E2KF|7uGy7Ʋ~hE{`]wdFGْ2d{/~hX1jvm yTFxEr-YdL|<8t}4XnzK<_ͤ-)ہh l[;Ԇ3/7!I|qF $3Wj?YKCEJ]i uk.^4|ښҶgYըU.MMw _mWYCzU++EFOOjvfnڔGzj_8Bv]ӑSe{ku7sGw+JOjnPW~jf[$ m_^iH'x f}3r)k}c 楏jypMcummzehH`;Z<c=䆪/7S\fbx`a`+[>wVқ6O%3ZvoۅȮ[ZkHZ?4 ;_e Q'3sG P><`IʽpQsv2ub9YJyeT&jǺ&vN=I+pƦb#@eo(uK!{?* <(K/bXTMў`}&3H'TTVK|(wY}#D}|{hDVlJ{v=\_ aC@C.y"!Z7?{ڞq٤nzLs-dD ϰoi䍅Q>Pb=*".q%?SGlϝ/%:'בrMltξ.FpԳ+%5,ӺԽyށpַjt6pա0"yc!yVh4"?:=Ψ|V+ϖ0ʉYx*q<PyTnҬ̌LlAC+9} '2 # <ʏ1bLEzXoE8>G fmSy U(D~<H;E4*./dacqrl^?W;xQx↦L6+?98j›KEl.0rs*j+n OTP!}博 ٔ҂s+u7$)gk 9^ʘh/00cnaumeAV*U/nyG6K@7b{Qvk_y\P Lzs2|n\;z$q;2^9W+u٬歵91z1Yz{!ͱyy((--CZUW/1mjeftro)P߅NvtH%,P6kD&Sh̕6m  ^ճVn?WG(v46}\,!켺cu߾&B k'A Dъ|>+nt=sixKwd?<աrXv/vP(1*Urϵ7l9խ'Nߺ>y*`fr:42h4h@dG}kF'-]\+*p=@2`$14w foT>"Qk/ߌ:v&L\z0샊cYbذfj\Uv@:a'DwL6` ]zL']Ag sh.χT[Rt{{f7f[~OL!9.B{mWv@ijFJ_֠`)*"8<r[R\ixäI=aa{C5魡vLJ윢BF[Z)ejS+ NC00wB4=} &WPQXrML,.ˢ^CfAh.:ZM\׳LFlYwhDeص~x%lIFhx`310g J3"kL|NLG+Z+ўö0 [arz+ o< ~FwmKNۅiBqxTN}8:DV;{uf.RsXBŴ>8aVTfREn6uN=GG} ×YT[}pGs: h`SGATUeT BFZ+-(7:Y7F4Mq7L늅.÷mTmI)͵ܴǮXQo" AXm NI6T,à|/ Y,҅xܛMVE0;쳤wsLsc %),fhA6){; *w'p+9_cI9iszhhZ@AN-l8Z-__q5طzu:CS4mcɗ躱ยp\րw@^xB Mv-W~Om%'U2 iW'F},i-}ҦXϫ;q] b xܟ!,``s?1ᵎ|yam@,Xx\,~u0G7 6G^.^1r2H&2JnMܟțHc' Xi<ߥ9-Ky7RG ֝xx<8ՈܤZT]U%0 ~UE[5>lW74fiG>psx/lvGz4mÛpNɡ-Չ}^ ,ƣ3CeF#6eDR춱9{4Qu콮hۉS^'c[Q_wHUGyޛ,GX# k^->L{s[~鵱-1{R/| & #_CV-SϜ+F'_"\T!P3:N?V99umG;I:1f_8(-y{&ZVU{k 2fl6Ol^ \CKB|tpz3!!LIhx9`(6:"7o?C:8 K\jfzxR+N7V>NI:_.(ʓ{6g-pƙGξRKkf)ܢJIm߰HSŸgCsy ͣRN@58J3$OVTq}|ϝj7r%د0$]tT=G{Ge;e(SPV;?cSF.-rk,$y ?_t^w?:Aybv({u< G)S׮}Hpw P-3C)*HԔ*ɣXC9+t: ^F/.8C3wΓK|ٻ DrScKv`Q LbT3CZdql\aP,G/1qoM8JѮ5T[\6bl#&-{o')5$PBҲ%dRą9jQ/{-m,˵i3 L^XZX]3:\աQ;<;(ζq`ŐVHRt[vӴ&p(> 4NVّm\[՛.ȮR4bSEo4ڐͺ-'5; s,r7w~n6ͥT}ԥӚɥ驐>Ң\;Z@,I9eeg̲L> Rh4ZH@1Gʈ⥧ mhofKtEo tsƔ;/L dkQ#MmDK[8^B=4lDbfTUKs9)Lj sV P׾Lx.`ϠWBi:Nb{ݵvvp)?H/16mO ΠbreZ bOs i.8&$oGP"!yRm?eZY{B>i1Eu |‹U,c3hKcI7Ze"TfXaF&in__7D#m o \C8}bJMɼǤTUfNDۊ-؜NuΨK5>8+~N29.>S|ճ|r5 )n,=e(IF#ÛqIZ<z)ǁh$ @ Am=N1D^^}?$:$'ߒD׵/K͋K[55yfTLczmx&hύ螊ζNJĀ~ Xz=6^ ʟVT:M+ ԁqTsmov -a6"W<[ -mlk.YF%yz{8*dB r C*p1D)3g[3,wz͑bT`@>)jx$8<.ٰ,D T^+]Y@p q[wJN!^" .qr${bc3USτ@hmOep6$#v_>Yn]EwWuyiYO7q6@ 5~K1ȋN߱8+1ζ瀧ł׉8t=*>M550bˑjQ bnPM(ŧ^/ᓓCV U6I)Юe&&S<6 ]/N uLI<;8};t[#j\(hj1v߸odeC7gM>̬ENph&Ƒ_ܞ(D,l0dG^v ' >I5%#m[*eTK}H$'"uBI_MXHX⾇i%ގЛ()3JOi̩<_ssO+ŵsS7;;^6kgxQ887UDW7ѻ;$_?[( e( LjT!S 0F_0+`@A#]e SnF&ݣ3AC i8.|^nAm%ԱpBR`_2sǥVi}9|޸q*YV熲3HiG9Wù-~j p*w5 q#N+N-g!8%ȂLx?}>Aa}~b\qtւ"gP{o DžBI/[bBʽ7𞶆_5J<3gV)@H!}X[CQd8:!²:+4gK4.XYs( iTǚlc' !?^LTPFf&i)V8LW~kצyy5l #H+pCMmPKHɄ_]OXNZZҫ䍅QE"$؛{(n$OfTOVx+#i=S`oZ2X^T7IFzqKߺ8'9"MXyFK]Y9Z"|cqŦUwn">FceU7s S+Nnrg/Pf p! e|TYPǡR2+KӬ0@xfcn-^# *V4>"͕ R(xK=k+liGg֥9sje1i&NxMف^켉QܐcИ~n&SF %(qE S>d)9bIۚ@92< i͍xR}L0/O;`ޖ(JcR(4ёӭ`^D F ;$AJ#}\ˎVdt.?ܷ IDATQzxo%YL*_T"g`Ox!`}ճtKys5x,gi'Q*. {Q~Ecimi2o=\aWQRm ܟPF(`#RFeVfTVfɑԈo^N(洟X`o |¢Nh<[Rl/ +^Vͮ;9^%wG4³Ɛ<bMoU}站ݴ%9s;8 䛀 @` B^Pv:4}6(qd<*@'oZ}Hx9nŋ4չ][Sl#ʥI6˾8 4}Rjee(#VTהLMrh`W]OSm Sn~ckt:rԳ,s՜zfΜ?W1cQɺI-O5zD{ +5/ӬoF.tolA¼Q ?i:yݷmR )"_l`G\Kݿ<㱑gcgPufˬV ,49 laJS_zq[đ`Qn#mPucSkIkani-/tCNf$z{Jg$e{q9}f  x03&{ x_we#sSmLaծuMzVM]#FRBPjk%>BpT6+x,1Û=a&Mf>OP*9Q.YGH)9&g/z{cJ?%O}!\=ƃw}5gW6 Jj~SYu{3olਫCa3 3-DB(ǭ,iD U#tzR8QW-alBU"xD9 ݤY\36ɃVrB!H%O?e3$G@>yc!y,'b*Ę"q1pE;}(͈8 8@">Q>y Sy#<wo h U\^*Heؼv MUl W~rp|ռ7ϗJ \aJTVB,!5+)VoH8Rjs֤Ar" 1%_`4`ʂT&i[_4& ql-FoFW#Y׺߽@6~dݸwI,\w>We4rWY[ksc2cBcPQZp3Z$_c@&ߜSq ?ڵJX`m L~\?8;P3la\X v%sMGidzvxpB?:BW5ݟc3k.Mqw(K/;X]A&>Bŷ9 {随<:KCs\|>ڊԕUw;7ӧ4yuG4[6o/ߊ{b qiCl&=HfHN/jmk=G{eXESˏ5]WkNQɷE[ZxL&M: t} 3]\Oo -h.Ӵ>>d2J)SZipzi-X]4b"{mbBeqy\-.2 @#vj꾞``4b D&m,nǮ/`+$F(H5@(&D9UPZY/ds gJ0=^ZU =aeNߒ ӓ^yxS ف-eU"K*z797@Y"af*k+;rw 5k^$Cq6†bؾ5jK7**N[sSys~4' ^ԫ iNTD׍纵"³ZhΆp&.uUm;qkdl+)(O={Ksd gi`oܻbr [O>6<{/] |RqP0`Kre|xjVsp߀r2Kęk**rFU2|*3hG;Z1׬kV%%O7zD˪jo-@͆-@q)+SR^MLoOjʼnJ')A]1QEEPy>yF 1ovw]2Y+FyQ̃#g_5WnnQu$նoN$CTlz[ObޅtXt"X|iM ֩tRJ(GT߃n&1* !ih`^k}rt}60(|wJtڷ& ihךJS-[\.^16 _^[=Xu ʔ\}(wciKHj)H6 SZxx&/,-Is,mШQVd g8bH+$)-iZ[vd'HIڶrr.-JKMdWPF0d:3(_y7}YRnchcqάsYV,a~0(a+ Vx,l=qR~Zms:J٘>0M#-5Nd5tZ5KǴwoZx\GѷOdZQlxjEiL߈9 ɓ*`0ؚóIeA O huvX4<>1 xX_pMx,.2vyʗvtк:ع9*h|)9uRY^Qh^dlpQ̬ENph&Ƒ_ܞ(D,l0dG^v ' >I5%#m[*eTK"R(䍅Q.{PB茀 2VS5' |K=d[r_\Z>;7elx94mVD +jH?zw|DVg)6sSm*|:d SSHKf~L(s"h$+ޢ XaȤ{tS#ONiӓT:+,OvnwDZƢ9h7"{ҹ9@rŹ1, M,ٖ'mx(Ԋ!ˍQ^}܌7r%G F%b{=8nAg4rQk 2΢RY!Ntek}ٽ<|X]+p4Q"eM+'"CD@0ॏYjO5\VPP)js^L-7(g"y.s̼}YLA|v J4P>qBC, hqEj~~tH^ݒQ錃&@zi_y[=$LG$ ip^RvGݬQGlզΪKœ ٩ N9X[ٔ\c\Kj,(i'dUγ+=Q)k{Ʋ+ =HY6)vR?95 EZdH~|VNCEtI`"ǥyA6*{#Tr~ET05١ ;w S!az fFiĿ凳!xkDYMҚ(EŕLH3'9櫁%edF-;ٚk2~4)FacFϴ2Lt3w`ʏ]&B1旖Q< iUꎥSO ܟ9[Pk (|(٬uJK 8n(F.bxL ňfVR;,^ gՑSau [}ti7>B՛_ uWޫu:Q6}\c%=z2Z]+u IDAT,Z{buMSo6Om[DȪaՐSߍq^Pj(W79I?NP7 BΚ!o)X;_"tבPx}Ѝ5jn9B,5ST̖純pxCoIKu"2 7xƂ8{l,Ax+"`[M#lڨU{󢎆>CU"P1iyq F!5f۸Z9ʚpo ;],co{ߍq"6 T~øPt"]qls,rFa;u(dD1<&fO_B# T6m# `CKJKDZPWrpc 3Czx_-,^@Ρ]?y{VnLF~zl(W~^6-RJʵ+G ݻ';{+B.n~рsr2E}h6) {Z,AߌMsn#u05^YP l?uqG*=#qK JiҫU m]c?3&KiϟhZ~LF d&GrТB25*V7vt$,P>rE}[cjpbnAZt?ͤUG;&vXG bji_scrtqy7s 4LzD~gks\]e^ݔ†P1j":lֲW"=Uⲯ<5h{sZ8 K+߼}&J(ٱ)RTP㟰y[Y%Uw~J# R5_ZO,U={qZ*۲}u#P˵qSXz#C4 Xhр 䭕kNhA9 ![H%X9^!U+iiPta ;15ak/0es:vvGNK"y6;6\fbawȥ3б} F;z 2?O\_:K XgksEY|}Z;̱@I%kklu܂2ޖ*9+ -MeCu2QTnlnnZ\I)$¶㶷2p5:Zjt́?mATl]t^RVn?0/΋pco_xZQ 2ЏbL Jq1 ,ZS%1<I;'Yb^X4c2%bVz?>X$pLVvћh0GdfXBZF"?$E3j]mcKhe '}셆+܄p6E45/(Yg'%e Tts(̬$# Xg+NxLm\Qw@Up }nRֿvBhț;l 99qLZ>2488'ᜭ䂜$~'3SmҺ.Vn]`+g3"Xje,(.m werZpFr{sm[ +l0iwOZ[F#Ԕi՞YKE3! a:o+,B&-咠9RRH\.:K:7c2ӣ >&[.#6 L~)ǒixek LQrBR6qmD^ɝi_369#bӺg h1Y}OɌ-LZ!gr)!6?%", 'i|gLON#k!09(형Y`D'D԰ѼN P3+?h奕{ݼZ ->Z(y%Q3-O(haehnxS|><)KXr(MX!<&0g^50|ꮿpeAO J*\zT$,/OʻU[[s1 }KEaY6)M1m QB33mVм9li sb|ﵖr(Yq[&C@'Me>\huN08&2N++Vؼԁ+OI ĒfI[M#H4pHHO tJ@}gl!5BJƂGb]9fyfk><6}vʏ2{T{K)ݧϜVG+h"AbP*0l-feGQTC:#zˡ#K=Cc ==wv9 \Zda~uՊ,N]-MC:1Y?t9|{^8eP,=Gy)n\ePKHi؂ 45קX0)8,BEdy[J; ǴKF6.h:C=g|( y]<#03q1G^&sE">C?#_ @#6ˇS"%FjCH mnU9!<4=-kPB ݶw/5B}olzZZTZ+0מn-=01iW THŋP2?684N.+4~%b1ֵy~ξn,j!!)i2 N.G. R`S WdSZ7fD'e0gG:VL%kIcY\O/l?'>6PMdV0f2rĵDB>T̛rzǧds*K^yܟpF* \N# X@˽e*T`2,۩v8&I//hTJ(C4r7L-9X- OggGQ'ǘq <öi.Ϥ]4xR-g@lv '"1`DzEC;;ְ̲‚ef \Ə %釥q1#R#!2'[\+.˷<ug4X_W'SÎ>7xߓe93Wޝe*ꦼ´dvMy![k 0nޜʔw\iC~fyB' [cc{{Uܘxm&7nGDGȇZ!۹ 1mQ'+̄Tγl?kYfËx8Zdu"ehr,cy ۪-khajL噱%yʎ T-C?2)ˁSʥ6RBɫ}*/o6w@[-x7/&l9;sgmp)28|{5hJAr /PfHDpzeirD۲<[5:<[$+8Aja57~J]1 N_8#1NceP 8l]&fZ~{Iqn N(IIZA"UjQN0x[P Vij5^>xnцm|؁nQvy\e9WX1_s<+$yb?\6t'-pMdlLNlCxㆻqdqd jit8K6= i]IY9uyD )*)_YWb3Eq赖BNRƂ8CwP{; `4C'QSgn&Sy /{Kyy/) ^Dips\;ëUjFg6c[UD(wwۗXܳ3qQ@Sj9(ǣC |@@ ~(̯yu oh F_rCƙ9cCHBiGP#ޒNE.B\+c5?3kJCpJ$|CCV hŚ]IުA DF._?e[TDW燒DxQUR )^#yӣSxO[ïkK|DR!="GEG:Lya^>cL aC2H!οo&ΨQWxDc4sYa~Ʃ X>-6@;6 vidYP+(8$4l +l^4;k?x٠O4r$h7X3hY}ZLre:}z廱;΀Y\9^;QEn g$vBَLTåkiRpbhV Na%TC.QMialUy#ם.Ne@0` V "yLUQ v-vw?ۓp::*Q8#Q+fb~NN (H Tᙍ܌m * m/Y̜HAZИ.\ȞvjzB@F&ANIVo@/ʏ2h̀8lSL 2ٌ9TD[3.ZXFz5΍-}0gJ|>$r|Vσ$"X0˻ # b #0x{ϔ2GS  K.-擝@Qt?'7zg< AqnL0FBKe w;3XG'1 ^b|Ь|iH GCV«FHK)Jjl6@AFbr9pl7cō=EQlT'EJGڜyE*o# oǭHj)3/x_b(G.ƹ @ BZzc>)hd@: "Z(B jz%hsS(ռ\4eqiZdKO^WV5IGF-4HZ.WfeQD7kh5[i꒱0'C~dzvyꂴSs6eo;~g.kb%~2JڪI%Y,wOfxDŚ AvBO7aM?ONMC?j>7&yQ!kj:?";qkkʞ8z}E 67 oMvΝcH^GQvolH33=Qv4Jbnqea0IkDNs:h2 s#MזlM5VJot ~CNσీO#gZ&:;W0}Ǯ^j!FܘoK(V*dkuҩ'`؜-B|U5en>lVM :GyQ 7ky#c1&bD3+)ltٳH鰺ԄJ>4 dF/y]+Վxz刨o>豒V_=y[W[ssͽQ:_&\Ʃ7l}B^xώD]Oէ-}"dLȰj)Ƃ8S( nΫᛎݤYS!gϝ/:ύHTv|^Zz <>F5!\΃O)*fKsZJ@tajjFpO-iu.ADuXg%oEliM*Vp`O2cq^J\*7&-/=."a(dc׻&lwACK~8GYq |3| `ﻱ;7BĆAoWW+mEQ(}R(&      1ф@@@@@@@@@`!ތrKhD @ @ @ @ @ @ @ @ @ `A@@@@@@@@@`C"`Ʋ-`$bl(aIi)YjYն]nl~af|?U+>7ŋc9'w/ۊO ":ںEJ[C@vH{dB#xO{E1X/0`N|VNF\X(7&"a#wOe~ Qun-bh-׺縺қ5)7. b^4iuجek]KE/{e_yjBSMbۍQcS?aJT(FlAAj^CەrY:1H=y<h݌$" 4jLphWPCS[LHPr s$-WFXźa5yxs,C02z-9$S:^(w{VBfA/䲸Tӌtqt/}6qaa6ZX~pi<|הLnD@J &p>یpUr}hl.5`=C{ZeXk0Ǧ&|u)s k7&8n.輤$~(a^ 6$AdŨji.b AXbK K2bx,vN)u/g h.;02eKŬ$~$}ֱ`&HĭB'tS7`̰D~HfI':ǖ6x{i#TNC WTQ lN!ij;^PKba.ΎOPKr9|kp Fuc;{YnfQmy7| 270''.iV;Z {,\{zfj}@YZʭ laqX @mCﮌ9YnU kJ ͭ3V%4ck|0KF n(+P|ﵖr(Yq[&C@'Me>\huR0(&nN++Vؼԁ+OI ĒfI[M#H4pHHO tJ@}Ovl!5BJF b]9ffk><6}vʏ2{T{K)ݧϜVGf3X"Ab QP 6ζPC({ NzБ%Qt^x0Svy]؞dT;K.-0?Z:l]jE g.h! ӟ~pn[=/2(ΞwI7Fhq]4]slAF̊o(:aMĬZW [{y<s #u׆dp`c/);WjhUbHf~dH/{_PʝZ?"W :du)ɁѦy~q~EhNM jƾiNh $aɁ~ǻ/**F">&Y_Π *8pc4\b,MW5 L/™uztOŏ%yfKtg@.z|5W#Ut V!5CFP}G~Qj]e1$a}ٺYoi1ᔑg1QLgTq;ʼNpkc{f2⬹X=604\ ZLQc{, ^5Qmo42L4Rآ(ցC4rQ-UyFdɦd@UtZ"Q$gd8=`oEB'UJZV)vn \JDn<|OcMӭ'8;5FE-CB<*Y8$GҎ냂}1QMl>} p-B^v.LAh̑cD.oy4H$qQ|zP,OMKlL!VB|ATs~K !-M}VhnQ&C !<6$vYGNJD]LYh;NkQ\qH?_{U@^)2P!/BH81ZG$fxrf "ǤZV 4[` 9RIxLx` hƦ53P":rVZh PK NNaΎt^ߋVB/4pc𚼍=ިnA ie1|fS)i1A\KJż))1/ww|J6;?Hʝg44B4j1 B &mo#"¿JÁbX+p-=D,wʓ[6 Vft:=Dȋš- tz<;$l^)rKbM;?`_Kٝj{S~vʑ N*HuxXg,ܣS8$eogͤ .V#Qx _hޗub2qLj?$:>~Xh04<Ec%t۪&1Ϥ]4 tKpIzH olбΎ5,`cEmٹY"i6W#CIau\̈%H>eD̺g;-z>"elݙ Չ԰ヲM+#{l0,$x`w'pʯ=xRSc7oNeʇ;T4t~n!AC?3}n;r\8WD |K [&Μl}g?44iL/-zzxDB 7?gD B(>'tDv$T26 Bt*ōL*D} F|Q~4Acf{bjf̡ 2qѢJ7;htntn8f>+6OȠP2?ĢY}m@YP^ȤP1,=hg쑏'4A *\Z'; {yA7;J-Mc~Nn| \p!cE=EQl<ĸQqz؞u Vގ3EVdR9$%#EB)xz6Rjjx-o u/l&qݕWR!KoԡaG׬[5.^+(}AQ:ZW&+:Tf[ oQ{?nE\TCuLy)F1=r|7@\/PqBC, hqEj~~tH^ݒQ錃&@zi_y[=$LG$ ip^RvGݬQGlզΪKœ ٩ N9X[ٔ\|p\Kj,(i'dUγ+=Q)k{Ʋ+ =HY6)vQ?95 EZdH~|VNCEtI`"ǥyA6*{#Tr~ET0u0 %w8CBO'8̌Ҷ gC<5OFpڕю6_ ,) 07tmٹ\iIH84<y7`>09޹+?vR&1T_ZFѶ&U'[;D+pon#@r),sdjZ9+; )7k z#c1&b:+)ltٳHBԄJ>4ueF/y]+ՎZz刨o>زV_=y[W[ssͽQ:_&\Ʃ7lomx=;u=U6п *I 7l"ߍqސ@JUMGnҏ)j"ڮ -+sgKFu$*;>/-T=lto[ο.n F}c9-% 05D5\#xx8[g=k]8 +ố;KnLlSyB[*Vp`O2cq^J\*7&-/=."a(dc׻&l -yeMD17F8Xb*A189Eu:K 2@@@@@@@THDx3/b*Ot m}/|exՙdǽw+J )N':f6fƷ++l;_$Zۃeᄑ,f ']}3#GۙchG;WqkמW_j" ^8cF><̋V==,rK*y=-EeTDz՟quhZe}\՗'_,o&Kxˑ{_]ѷþANSO"p~j= K),GT~=rfkkS􃳪xYKc6b8f{"}]QA, _Ip7Vݥ j7LW]u_{yDjTUgQG @ p^o]Nxo9sO[o#/"O*~ަ;G짦=' #lwO|=6|w7o%/^|<ؕ©,֐{w Sn|g,4*C@4 RB{=|x7cf.Gh[rlעrԼz,w}wW]wodxSR%ޟx4 i~HP٩Jaя4!ܲ͆p!R5wWĹZo@goɴ,^d4lͯ<';ĪŅV?7v~߽~\bGROk'ƕ7eRm7"VkUScqU\+27/!+}/ g 40ЀX?=A;{Wm@,ל;=&Ϯ 36ٞz$vA(W/Щ,MCаB 进&"%O0f~g^{Fݺ ۏ\ v5 *S훙-sjE!V) mOTޟ_OB>R #8X;6e&GZkۿp@/`4<}}_>0x߽kd0"`$=k+~v۬jW&`iE##,w-,KLT yH?&GDSxx "DGѳ7ڍ[x0'X7>•D-7`z U`svIZ:&9.ԗ=>9]}7q.xNv%3[gl4sFtĦn-kۗ=)~pXG?{ؑ~SʱчYk IDAT~gO=>-?slO~IVQѝy#<V ~s@i@ iT#Ю䡿~#ך9G}7~jAHGֱ3ݮ GJ0B]_K:=p𣿬K4iln>t>XOw{ExjC]oCQ]+ZΣ/>M~o)"h?vAi4! 73&\0pn?pnbA8'ze>j3X+p-^׶w Xhw~w~dfѠSI}A<^ӂ4Ցd_fR=G0k EQ9cRд_|M kVčC6ф@@`u.X8>;#D'~6I{$B!Hd +4-~}==a>] tGٟfc0TsvFAwmHdG˧zgJq˿Dn_>yyAxa Ѻg-R2WR;RRZq!KtGU}= W̙{J, yavg/֞o~/H'ֺ]IOg;;[H;{!NFzҮDLO>kD6B?~]koIkq-@Mب!o|ciJV!އXւěq5HyG䳳5?ŃX `/i*[+#C>CA ,0yrƤohZŧg:&m<]}8M|{5hJn*X٪}ёJ {2/퍇S' ZYM$e\'s.e+񌳃{4mFoR`z{MFA,P.,@A+˦W!!8%q'CM،>wo}%^ Q咠ٕٙZgCꅅ(vzy!NcE `ja9>iǡMH \)yx9-&tĀHjIKv~cˁ'M)a,inZ36~hho$5^Roy>Y(?wUpVoR U|AoAL'2˃YNrWxh0ĕ+st)ṼM@.Nkk 3CR|ɇ~~˾ Ui遁tV}TݟAZ 3E`ZZ3x,nLV.[I!u")jU G{Ki|d@`"ywfU^nj6# L TJ%Re I ʺr|r3Z>z-î <+ g/V!(*-Q8f  ЂYalH|]%h m~U|qꊾt?zt U^s-qU2rҠe_@^v9E_f}i/~ZwBj}oJUk!G')3-N^~'H{3A(p΍zւ}[Z7FB"+ Jz:rc ?]i5!K+Z`/iݢl (^ᔳf}}LxrQ2n|dXDk[T_})m݌+3oS'p߽/]r4t%;ѨJ;{"ȼ}w&$BJV+r@5z;%<1'}]~~p}K>cރIvE'+ ?7vcaEЋitz<-LN䅙//̦HQ=$`]oG6 7n.{.b[k),Vh>l[]!T ¨ڎaOk=G 0ra:b}~g3i ^Qƨ[d#+ U/, csyG ",7I6gϞ˟Ǟҟ?ELw^O7\O]|:"?S/=r{}ƺ<[m%&ѓ,BC hf~~H1vzgHg ZkH]Ny\Jwm@g$ @#OP[4hz\^{=AD p0Ӫ+#ʞPuJב=͔y 1;6q Dko*Omi+$u7=DoOVQ _WU'ّoWy—P@!@ZIfncp|;yyb-T^+>iS49?m(\UW(&       b*|1Bn|7KB.n?P;د +xֺ:f%q!Ȳ4:@zӿ?鋚!~ӦZQd:5A1-`w:9:8 ӵpH/] S`)K?㗼磛[rWz[z!Fbj;5vS:f㑾uX:Lv|}wኹ`Kم})@ȥRz׋2ޮOczL"'V=몤R Nݰ۹e, J|sʳVu專)GC$Db  r4$I8hB#XN>C 7+AgGwC0$ @\7݊ĩzWh#HKb5,rG _H洴F9vskK4Wsʚ%F7٨Xf$-iH0;Ia8Ãתqktd=v8\޾/~=f5?mA)"*/8-3YfTf;;1oGcu9z٣^oXܧ.?$.u X`cW(%OqvyyGD-澱Y*LXL[;}aک[gF/WM5@[U^Bӹ;NYid$ 2΢\wa:S`R!vYV- e%aK!*u֋ 3oR$문N[;i=q)c\](d7xKQ%j²/_k`Xi &.+/r&vdE@`O^+^Wj {(x;aYmc|#&cs"wʖ6##Mw9梱u|_?l!~gY~vA6.G_-<{I# I|`8ynyA7AxzHH*5;oAA5 ?j [^#wvf&4m6}Ʋ]).;mhg-t;e-VN_i ‚ؗ͝5o] Vt\ܮRZ(={zi]~l),;\2t~=Y7 @@ ]y…cHdU a3-.md29Mq<$$&rfe"U*`BeleH,b N$dR5U˴w74E1d \#b> tܹ$%ÒuI2y'+ћeɄu;ݳ#ܱX%;??=.U1O5}3uAd']qt.vp#=~C_9c{MwS=yZ,/xS))*893 $ |>s7ޱM)'lcģt:,_uV7z񢣩ҏS|>Zs;43g}& |SoNORzT;p=F$Ӑ^f :z`҄YJ^ 0F;s3eeI,]Ytz :hjdCTSSaIyhvNPiRRB?q?޹4т{E!|u*J@j}WQUA4ZǶzMXUUbΉ8u?A]C ܅FkCڗM,T*tu-6C6B`w=qXX'4D3polSG\ǍwR6mӍ;]ilПy}ͦL-V_@ NЎܮOr:`qǕ3FGh.aP @?JOW^vc270.tѱ2~̺zH[q`*=rn?qCDؕ/8xD_ iR1.>U).o,{={X4$E7 :8& i~CKWt*6y$bC܅bG[Cys)W!F\ZrnR}'ӪacY54p}-',a{#LBA<[bΊS;,B~ Ԫ sHUC! ]|X Rs9ѯn))ОG0!Peef6n=}ބq8KH+/vvLT/]H(6^Υ; H^IqjKޱhY_ rtaЖw?He*DРc)>IrFޢƠ H۵ζxP]-s4@v&IDATh9_ -/&Z_>c,q 6NC KUF~уF]h[܇$V$mp=uIgn0q JˡVu C#¢N)옳!,.] `V>ekZ /o5K&QҔXu,I$y%)#Fa߿m((Zt" 42Aab{iLsG1&YO@_l'O5 ANxPK+Z.P" ga( HSw n[vfߝڮV&\8?Ej6}\ 'HU,`ύsltUȪz6>kf d_8X\]ľ-K!4P:!kԒ5Ut-fXqNczmbXƧqrq4&\:΀=kGʄt<Xi(3hn8hvzVVñn$I(eNJw DRl*.s6Guҡ0@m+ҖiS'_؄OQE]]#Z*$'}ۼH"{*_~>tƅ..(rg] U#B蓷}p&d6վ=*SY,Wq,ee9KW?F'=\9Xّ!;FQ˞ C;OMsUpa4p-i {/Kyz#ĭ-ܮR[98zÅ}UE NK F?Ǯ%}M 1B$ @t JnW6) ߕ1Kh>e.S۱ZQyA³{!~U;<o c'BFa}b\&5'}o @C@tMS/Fګ t=5|50ϩnose itching intensifies"); error!("HATCHOOO!"); debug!("encountered minor problem, trying to recover"); info!("gesundheit"); debug!("recovered from minor problem, continuing"); } else if 25 == i { info!("successfully loaded nothing"); info!("have a good time!"); } } } // ===================== Logging Set Up ===================== fn set_up_logging() { // configure colors for the whole line let colors_line = ColoredLevelConfig::new() .error(Color::Red) .warn(Color::Yellow) // we actually don't need to specify the color for debug and info, they are white by default .info(Color::White) .debug(Color::White) // depending on the terminals color scheme, this is the same as the background color .trace(Color::BrightBlack); // configure colors for the name of the level. // since almost all of them are the some as the color for the whole line, we // just clone `colors_line` and overwrite our changes let colors_level = colors_line.clone().info(Color::Green); // here we set up our fern Dispatch fern::Dispatch::new() .format(move |out, message, record| { out.finish(format_args!( "{color_line}[{date}][{target}][{level}{color_line}] {message}\x1B[0m", color_line = format_args!("\x1B[{}m", colors_line.get_color(&record.level()).to_fg_str()), date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), target = record.target(), level = colors_level.color(record.level()), message = message, )); }) // set the default log level. to filter out verbose log messages from dependencies, set // this to Warn and overwrite the log level for your crate. .level(log::LevelFilter::Warn) // change log levels for individual modules. Note: This looks for the record's target // field which defaults to the module path but can be overwritten with the `target` // parameter: // `info!(target="special_target", "This log message is about special_target");` .level_for("pretty_colored", log::LevelFilter::Trace) // output to stdout .chain(std::io::stdout()) .apply().unwrap(); debug!("finished setting up logging! yay!"); } fern-0.5.8/examples/syslog.rs010064400017500000144000000024521344236521500144070ustar0000000000000000extern crate fern; #[macro_use] extern crate log; #[cfg(not(windows))] extern crate syslog; #[cfg(not(windows))] fn setup_logging() -> Result<(), Box> { let syslog_fmt = syslog::Formatter3164 { facility: syslog::Facility::LOG_USER, hostname: None, process: "fern-syslog-example".into(), pid: 0, }; fern::Dispatch::new() // by default only accept warning messages so as not to spam .level(log::LevelFilter::Warn) // but accept Info if we explicitly mention it .level_for("explicit-syslog", log::LevelFilter::Info) .chain(syslog::unix(syslog_fmt)?) .apply()?; Ok(()) } #[cfg(not(windows))] fn main() { setup_logging().expect("failed to initialize logging."); // None of this will be shown in the syslog: for i in 0..5 { info!("executing section: {}", i); debug!("section {} 1/4 complete.", i); debug!("section {} 1/2 complete.", i); debug!("section {} 3/4 complete.", i); info!("section {} completed!", i); } // these two *will* show. info!(target: "explicit-syslog", "hello to the syslog! this is rust."); warn!("AHHH something's on fire."); } #[cfg(windows)] fn main() { panic!("this example does not work on Windows."); } fern-0.5.8/examples/syslog3.rs010064400017500000144000000023121344236521500144650ustar0000000000000000extern crate fern; #[macro_use] extern crate log; #[cfg(not(windows))] // This is necessary because `fern` depends on both version 3 and 4. extern crate syslog3 as syslog; #[cfg(not(windows))] fn setup_logging() -> Result<(), fern::InitError> { fern::Dispatch::new() // by default only accept warning messages so as not to spam .level(log::LevelFilter::Warn) // but accept Info if we explicitly mention it .level_for("explicit-syslog", log::LevelFilter::Info) .chain(syslog::unix(syslog::Facility::LOG_USER)?) .apply()?; Ok(()) } #[cfg(not(windows))] fn main() { setup_logging().expect("failed to initialize logging."); // None of this will be shown in the syslog: for i in 0..5 { info!("executing section: {}", i); debug!("section {} 1/4 complete.", i); debug!("section {} 1/2 complete.", i); debug!("section {} 3/4 complete.", i); info!("section {} completed!", i); } // these two *will* show. info!(target: "explicit-syslog", "hello to the syslog! this is rust."); warn!("AHHH something's on fire."); } #[cfg(windows)] fn main() { panic!("this example does not work on Windows."); } fern-0.5.8/src/builders.rs010064400017500000144000001174401344233215400136510ustar0000000000000000use std::borrow::Cow; use std::io::Write; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::{cmp, fmt, fs, io}; #[cfg(all(not(windows), feature = "syslog-4"))] use std::collections::HashMap; use log::{self, Log}; use {log_impl, Filter, FormatCallback, Formatter}; #[cfg(all(not(windows), feature = "syslog-3"))] use syslog_3; #[cfg(all(not(windows), feature = "syslog-4"))] use {Syslog4Rfc3164Logger, Syslog4Rfc5424Logger}; /// The base dispatch logger. /// /// This allows for formatting log records, limiting what records can be passed /// through, and then dispatching records to other dispatch loggers or output /// loggers. /// /// Note that all methods are position-insensitive. /// `Dispatch::new().format(a).chain(b)` produces the exact same result /// as `Dispatch::new().chain(b).format(a)`. Given this, it is preferred to put /// 'format' and other modifiers before 'chain' for the sake of clarity. /// /// Example usage demonstrating all features: /// /// ```no_run /// # // no_run because this creates log files. /// #[macro_use] /// extern crate log; /// extern crate fern; /// /// use std::{fs, io}; /// /// # fn setup_logger() -> Result<(), fern::InitError> { /// fern::Dispatch::new() /// .format(|out, message, record| { /// out.finish(format_args!( /// "[{}][{}] {}", /// record.level(), /// record.target(), /// message, /// )) /// }) /// .chain( /// fern::Dispatch::new() /// // by default only accept warn messages /// .level(log::LevelFilter::Warn) /// // accept info messages from the current crate too /// .level_for("my_crate", log::LevelFilter::Info) /// // `io::Stdout`, `io::Stderr` and `io::File` can be directly passed in. /// .chain(io::stdout()), /// ) /// .chain( /// fern::Dispatch::new() /// // output all messages /// .level(log::LevelFilter::Trace) /// // except for hyper, in that case only show info messages /// .level_for("hyper", log::LevelFilter::Info) /// // `log_file(x)` equates to /// // `OpenOptions::new().write(true).append(true).create(true).open(x)` /// .chain(fern::log_file("persistent-log.log")?) /// .chain(fs::OpenOptions::new() /// .write(true) /// .create(true) /// .truncate(true) /// .create(true) /// .open("/tmp/temp.log")?), /// ) /// .chain( /// fern::Dispatch::new() /// .level(log::LevelFilter::Error) /// .filter(|_meta_data| { /// // as an example, randomly reject half of the messages /// # /* /// rand::random() /// # */ /// # true /// }) /// .chain(io::stderr()), /// ) /// // and finally, set as the global logger! /// .apply()?; /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger") } /// ``` #[must_use = "this is only a logger configuration and must be consumed with into_log() or apply()"] pub struct Dispatch { format: Option>, children: Vec, default_level: log::LevelFilter, levels: Vec<(Cow<'static, str>, log::LevelFilter)>, filters: Vec>, } /// Logger which is usable as an output for multiple other loggers. /// /// This struct contains a built logger stored in an [`Arc`], and can be /// safely cloned. /// /// See [`Dispatch::into_shared`]. /// /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html /// [`Dispatch::into_shared`]: struct.Dispatch.html#method.into_shared #[derive(Clone)] pub struct SharedDispatch { inner: Arc, min_level: log::LevelFilter, } impl Dispatch { /// Creates a dispatch, which will initially do nothing. #[inline] pub fn new() -> Self { Dispatch { format: None, children: Vec::new(), default_level: log::LevelFilter::Trace, levels: Vec::new(), filters: Vec::new(), } } /// Sets the formatter of this dispatch. The closure should accept a /// callback, a message and a log record, and write the resulting /// format to the writer. /// /// The log record is passed for completeness, but the `args()` method of /// the record should be ignored, and the [`fmt::Arguments`] given /// should be used instead. `record.args()` may be used to retrieve the /// _original_ log message, but in order to allow for true log /// chaining, formatters should use the given message instead whenever /// including the message in the output. /// /// To avoid all allocation of intermediate results, the formatter is /// "completed" by calling a callback, which then calls the rest of the /// logging chain with the new formatted message. The callback object keeps /// track of if it was called or not via a stack boolean as well, so if /// you don't use `out.finish` the log message will continue down /// the logger chain unformatted. /// /// [`fmt::Arguments`]: https://doc.rust-lang.org/std/fmt/struct.Arguments.html /// /// Example usage: /// /// ``` /// fern::Dispatch::new().format(|out, message, record| { /// out.finish(format_args!( /// "[{}][{}] {}", /// record.level(), /// record.target(), /// message /// )) /// }) /// # .into_log(); /// ``` #[inline] pub fn format(mut self, formatter: F) -> Self where F: Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static, { self.format = Some(Box::new(formatter)); self } /// Adds a child to this dispatch. /// /// All log records which pass all filters will be formatted and then sent /// to all child loggers in sequence. /// /// Note: If the child logger is also a Dispatch, and cannot accept any log /// records, it will be dropped. This only happens if the child either /// has no children itself, or has a minimum log level of /// [`LevelFilter::Off`]. /// /// [`LevelFilter::Off`]: https://docs.rs/log/0.4/log/enum.LevelFilter.html#variant.Off /// /// Example usage: /// /// ``` /// fern::Dispatch::new() /// .chain(fern::Dispatch::new().chain(std::io::stdout())) /// # .into_log(); /// ``` #[inline] pub fn chain>(mut self, logger: T) -> Self { self.children.push(logger.into().0); self } /// Sets the overarching level filter for this logger. All messages not /// already filtered by something set by [`Dispatch::level_for`] will /// be affected. /// /// All messages filtered will be discarded if less severe than the given /// level. /// /// Default level is [`LevelFilter::Trace`]. /// /// [`Dispatch::level_for`]: #method.level_for /// [`LevelFilter::Trace`]: https://docs.rs/log/0.4/log/enum.LevelFilter.html#variant.Trace /// /// Example usage: /// /// ``` /// # extern crate log; /// # extern crate fern; /// # /// # fn main() { /// fern::Dispatch::new() /// .level(log::LevelFilter::Info) /// # .into_log(); /// # } /// ``` #[inline] pub fn level(mut self, level: log::LevelFilter) -> Self { self.default_level = level; self } /// Sets a per-target log level filter. Default target for log messages is /// `crate_name::module_name` or /// `crate_name` for logs in the crate root. Targets can also be set with /// `info!(target: "target-name", ...)`. /// /// For each log record fern will first try to match the most specific /// level_for, and then progressively more general ones until either a /// matching level is found, or the default level is used. /// /// For example, a log for the target `hyper::http::h1` will first test a /// level_for for `hyper::http::h1`, then for `hyper::http`, then for /// `hyper`, then use the default level. /// /// Examples: /// /// A program wants to include a lot of debugging output, but the library /// "hyper" is known to work well, so debug output from it should be /// excluded: /// /// ``` /// # extern crate log; /// # extern crate fern; /// # /// # fn main() { /// fern::Dispatch::new() /// .level(log::LevelFilter::Trace) /// .level_for("hyper", log::LevelFilter::Info) /// # .into_log(); /// # } /// ``` /// /// A program has a ton of debug output per-module, but there is so much /// that debugging more than one module at a time is not very useful. /// The command line accepts a list of modules to debug, while keeping the /// rest of the program at info level: /// /// ``` /// # extern crate log; /// # extern crate fern; /// # /// fn setup_logging(verbose_modules: T) -> Result<(), fern::InitError> /// where /// I: AsRef, /// T: IntoIterator, /// { /// let mut config = fern::Dispatch::new() /// .level(log::LevelFilter::Info); /// /// for module_name in verbose_modules { /// config = config.level_for( /// format!("my_crate_name::{}", module_name.as_ref()), /// log::LevelFilter::Debug, /// ); /// } /// /// config.chain(std::io::stdout()).apply()?; /// /// Ok(()) /// } /// # /// # // we're ok with apply() failing. /// # fn main() { let _ = setup_logging(&["hi"]); } /// ``` #[inline] pub fn level_for>>( mut self, module: T, level: log::LevelFilter, ) -> Self { let module = module.into(); if let Some((index, _)) = self .levels .iter() .enumerate() .find(|&(_, &(ref name, _))| name == &module) { self.levels.remove(index); } self.levels.push((module, level)); self } /// Adds a custom filter which can reject messages passing through this /// logger. /// /// The logger will continue to process log records only if all filters /// return `true`. /// /// [`Dispatch::level`] and [`Dispatch::level_for`] are preferred if /// applicable. /// /// [`Dispatch::level`]: #method.level /// [`Dispatch::level_for`]: #method.level_for /// /// Example usage: /// /// This sends error level messages to stderr and others to stdout. /// /// ``` /// # extern crate log; /// # extern crate fern; /// # /// # fn main() { /// fern::Dispatch::new() /// .level(log::LevelFilter::Info) /// .chain( /// fern::Dispatch::new() /// .filter(|metadata| { /// // Reject messages with the `Error` log level. /// metadata.level() != log::LevelFilter::Error /// }) /// .chain(std::io::stderr()), /// ) /// .chain( /// fern::Dispatch::new() /// .level(log::LevelFilter::Error) /// .chain(std::io::stdout()), /// ) /// # .into_log(); /// # } #[inline] pub fn filter(mut self, filter: F) -> Self where F: Fn(&log::Metadata) -> bool + Send + Sync + 'static, { self.filters.push(Box::new(filter)); self } /// Builds this dispatch and stores it in a clonable structure containing /// an [`Arc`]. /// /// Once "shared", the dispatch can be used as an output for multiple other /// dispatch loggers. /// /// Example usage: /// /// This separates info and warn messages, sending info to stdout + a log /// file, and warn to stderr + the same log file. Shared is used so the /// program only opens "file.log" once. /// /// ```no_run /// # extern crate log; /// # extern crate fern; /// # /// # fn setup_logger() -> Result<(), fern::InitError> { /// /// let file_out = fern::Dispatch::new() /// .chain(fern::log_file("file.log")?) /// .into_shared(); /// /// let info_out = fern::Dispatch::new() /// .level(log::LevelFilter::Debug) /// .filter(|metadata| /// // keep only info and debug (reject warn and error) /// metadata.level() <= log::Level::Info) /// .chain(std::io::stdout()) /// .chain(file_out.clone()); /// /// let warn_out = fern::Dispatch::new() /// .level(log::LevelFilter::Warn) /// .chain(std::io::stderr()) /// .chain(file_out); /// /// fern::Dispatch::new() /// .chain(info_out) /// .chain(warn_out) /// .apply(); /// /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html pub fn into_shared(self) -> SharedDispatch { let (min_level, dispatch) = self.into_dispatch(); SharedDispatch { inner: Arc::new(dispatch), min_level: min_level, } } /// Builds this into the actual logger implementation. /// /// This could probably be refactored, but having everything in one place /// is also nice. fn into_dispatch(self) -> (log::LevelFilter, log_impl::Dispatch) { let Dispatch { format, children, default_level, levels, mut filters, } = self; let mut max_child_level = log::LevelFilter::Off; let output = children .into_iter() .flat_map(|child| match child { OutputInner::Stdout { stream, line_sep } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Stdout(log_impl::Stdout { stream: stream, line_sep: line_sep, })) } OutputInner::Stderr { stream, line_sep } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Stderr(log_impl::Stderr { stream: stream, line_sep: line_sep, })) } OutputInner::File { stream, line_sep } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::File(log_impl::File { stream: Mutex::new(io::BufWriter::new(stream)), line_sep: line_sep, })) } OutputInner::Writer { stream, line_sep } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Writer(log_impl::Writer { stream: Mutex::new(stream), line_sep: line_sep, })) } OutputInner::Sender { stream, line_sep } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Sender(log_impl::Sender { stream: Mutex::new(stream), line_sep: line_sep, })) } #[cfg(all(not(windows), feature = "syslog-3"))] OutputInner::Syslog3(log) => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Syslog3(log_impl::Syslog3 { inner: log })) } #[cfg(all(not(windows), feature = "syslog-4"))] OutputInner::Syslog4Rfc3164(logger) => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Syslog4Rfc3164(log_impl::Syslog4Rfc3164 { inner: Mutex::new(logger), })) } #[cfg(all(not(windows), feature = "syslog-4"))] OutputInner::Syslog4Rfc5424 { logger, transform } => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Syslog4Rfc5424(log_impl::Syslog4Rfc5424 { inner: Mutex::new(logger), transform, })) } OutputInner::Panic => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::Panic(log_impl::Panic)) } OutputInner::Dispatch(child_dispatch) => { let (child_level, child) = child_dispatch.into_dispatch(); if child_level > log::LevelFilter::Off { max_child_level = cmp::max(max_child_level, child_level); Some(log_impl::Output::Dispatch(child)) } else { None } } OutputInner::SharedDispatch(child_dispatch) => { let SharedDispatch { inner: child, min_level: child_level, } = child_dispatch; if child_level > log::LevelFilter::Off { max_child_level = cmp::max(max_child_level, child_level); Some(log_impl::Output::SharedDispatch(child)) } else { None } } OutputInner::OtherBoxed(child_log) => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::OtherBoxed(child_log)) } OutputInner::OtherStatic(child_log) => { max_child_level = log::LevelFilter::Trace; Some(log_impl::Output::OtherStatic(child_log)) } }) .collect(); let min_level = levels .iter() .map(|t| t.1) .max() .map_or(default_level, |lvl| cmp::max(lvl, default_level)); let real_min = cmp::min(min_level, max_child_level); filters.shrink_to_fit(); let dispatch = log_impl::Dispatch { output: output, default_level: default_level, levels: levels.into(), format: format, filters: filters, }; (real_min, dispatch) } /// Builds this logger into a `Box` and calculates the minimum /// log level needed to have any effect. /// /// While this method is exposed publicly, [`Dispatch::apply`] is typically /// used instead. /// /// The returned LevelFilter is a calculation for all level filters of this /// logger and child loggers, and is the minimum log level needed to /// for a record to have any chance of passing through this logger. /// /// [`Dispatch::apply`]: #method.apply /// /// Example usage: /// /// ``` /// # extern crate log; /// # extern crate fern; /// # /// # fn main() { /// let (min_level, log) = fern::Dispatch::new() /// .level(log::LevelFilter::Info) /// .chain(std::io::stdout()) /// .into_log(); /// /// assert_eq!(min_level, log::LevelFilter::Info); /// # } /// ``` pub fn into_log(self) -> (log::LevelFilter, Box) { let (level, logger) = self.into_dispatch(); if level == log::LevelFilter::Off { (level, Box::new(log_impl::Null)) } else { (level, Box::new(logger)) } } /// Builds this logger and instantiates it as the global [`log`] logger. /// /// # Errors: /// /// This function will return an error if a global logger has already been /// set to a previous logger. /// /// [`log`]: https://github.com/rust-lang-nursery/log pub fn apply(self) -> Result<(), log::SetLoggerError> { let (max_level, log) = self.into_log(); log::set_boxed_logger(log)?; log::set_max_level(max_level); Ok(()) } } /// This enum contains various outputs that you can send messages to. enum OutputInner { /// Prints all messages to stdout with `line_sep` separator. Stdout { stream: io::Stdout, line_sep: Cow<'static, str>, }, /// Prints all messages to stderr with `line_sep` separator. Stderr { stream: io::Stderr, line_sep: Cow<'static, str>, }, /// Writes all messages to file with `line_sep` separator. File { stream: fs::File, line_sep: Cow<'static, str>, }, /// Writes all messages to the writer with `line_sep` separator. Writer { stream: Box, line_sep: Cow<'static, str>, }, /// Writes all messages to mpst::Sender with `line_sep` separator. Sender { stream: Sender, line_sep: Cow<'static, str>, }, /// Passes all messages to other dispatch. Dispatch(Dispatch), /// Passes all messages to other dispatch that's shared. SharedDispatch(SharedDispatch), /// Passes all messages to other logger. OtherBoxed(Box), /// Passes all messages to other logger. OtherStatic(&'static Log), /// Passes all messages to the syslog. #[cfg(all(not(windows), feature = "syslog-3"))] Syslog3(syslog_3::Logger), /// Passes all messages to the syslog. #[cfg(all(not(windows), feature = "syslog-4"))] Syslog4Rfc3164(Syslog4Rfc3164Logger), /// Sends all messages through the transform then passes to the syslog. #[cfg(all(not(windows), feature = "syslog-4"))] Syslog4Rfc5424 { logger: Syslog4Rfc5424Logger, transform: Box< Fn(&log::Record) -> (i32, HashMap>, String) + Sync + Send, >, }, /// Panics with messages text for all messages. Panic, } /// Logger which will panic whenever anything is logged. The panic /// will be exactly the message of the log. /// /// `Panic` is useful primarily as a secondary logger, filtered by warning or /// error. /// /// # Examples /// /// This configuration will output all messages to stdout and panic if an Error /// message is sent. /// /// ``` /// # extern crate fern; /// # extern crate log; /// fern::Dispatch::new() /// // format, etc. /// .chain(std::io::stdout()) /// .chain( /// fern::Dispatch::new() /// .level(log::LevelFilter::Error) /// .chain(fern::Panic) /// ) /// # /* /// .apply()?; /// # */ .into_log(); /// ``` /// /// This sets up a "panic on warn+" logger, and ignores errors so it can be /// called multiple times. /// /// This might be useful in test setup, for example, to disallow warn-level /// messages. /// /// ```no_run /// # extern crate fern; /// # extern crate log; /// fn setup_panic_logging() { /// fern::Dispatch::new() /// .level(log::LevelFilter::Warn) /// .chain(fern::Panic) /// .apply() /// // ignore errors from setting up logging twice /// .ok(); /// } /// ``` pub struct Panic; /// Configuration for a logger output. pub struct Output(OutputInner); impl From for Output { /// Creates an output logger forwarding all messages to the dispatch. fn from(log: Dispatch) -> Self { Output(OutputInner::Dispatch(log)) } } impl From for Output { /// Creates an output logger forwarding all messages to the dispatch. fn from(log: SharedDispatch) -> Self { Output(OutputInner::SharedDispatch(log)) } } impl From> for Output { /// Creates an output logger forwarding all messages to the custom logger. fn from(log: Box) -> Self { Output(OutputInner::OtherBoxed(log)) } } impl From<&'static Log> for Output { /// Creates an output logger forwarding all messages to the custom logger. fn from(log: &'static Log) -> Self { Output(OutputInner::OtherStatic(log)) } } impl From for Output { /// Creates an output logger which writes all messages to the file with /// `\n` as the separator. /// /// File writes are buffered and flushed once per log record. fn from(file: fs::File) -> Self { Output(OutputInner::File { stream: file, line_sep: "\n".into(), }) } } impl From> for Output { /// Creates an output logger which writes all messages to the writer with /// `\n` as the separator. /// /// This does no buffering and it is up to the writer to do buffering as /// needed (eg. wrap it in `BufWriter`). However, flush is called after /// each log record. fn from(writer: Box) -> Self { Output(OutputInner::Writer { stream: writer, line_sep: "\n".into(), }) } } impl From for Output { /// Creates an output logger which writes all messages to stdout with the /// given handle and `\n` as the separator. fn from(stream: io::Stdout) -> Self { Output(OutputInner::Stdout { stream: stream, line_sep: "\n".into(), }) } } impl From for Output { /// Creates an output logger which writes all messages to stderr with the /// given handle and `\n` as the separator. fn from(stream: io::Stderr) -> Self { Output(OutputInner::Stderr { stream: stream, line_sep: "\n".into(), }) } } impl From> for Output { /// Creates an output logger which writes all messages to the given /// mpsc::Sender with '\n' as the separator. /// /// All messages sent to the mpsc channel are suffixed with '\n'. fn from(stream: Sender) -> Self { Output(OutputInner::Sender { stream: stream, line_sep: "\n".into(), }) } } #[cfg(all(not(windows), feature = "syslog-3"))] impl From for Output { /// Creates an output logger which writes all messages to the given syslog /// output. /// /// Log levels are translated trace => debug, debug => debug, info => /// informational, warn => warning, and error => error. /// /// This requires the `"syslog-3"` feature. fn from(log: syslog_3::Logger) -> Self { Output(OutputInner::Syslog3(log)) } } #[cfg(all(not(windows), feature = "syslog-3"))] impl From> for Output { /// Creates an output logger which writes all messages to the given syslog /// output. /// /// Log levels are translated trace => debug, debug => debug, info => /// informational, warn => warning, and error => error. /// /// Note that while this takes a Box for convenience (syslog /// methods return Boxes), it will be immediately unboxed upon storage /// in the configuration structure. This will create a configuration /// identical to that created by passing a raw `syslog::Logger`. /// /// This requires the `"syslog-3"` feature. fn from(log: Box) -> Self { Output(OutputInner::Syslog3(*log)) } } #[cfg(all(not(windows), feature = "syslog-4"))] impl From for Output { /// Creates an output logger which writes all messages to the given syslog. /// /// Log levels are translated trace => debug, debug => debug, info => /// informational, warn => warning, and error => error. /// /// Note that due to https://github.com/Geal/rust-syslog/issues/41, /// logging to this backend requires one allocation per log call. /// /// This is for RFC 3164 loggers. To use an RFC 5424 logger, use the /// [`Output::syslog_5424`] helper method. /// /// This requires the `"syslog-4"` feature. fn from(log: Syslog4Rfc3164Logger) -> Self { Output(OutputInner::Syslog4Rfc3164(log)) } } impl From for Output { /// Creates an output logger which will panic with message text for all /// messages. fn from(_: Panic) -> Self { Output(OutputInner::Panic) } } impl Output { /// Returns a file logger using a custom separator. /// /// If the default separator of `\n` is acceptable, an [`fs::File`] /// instance can be passed into [`Dispatch::chain`] directly. /// /// ```no_run /// # fn setup_logger() -> Result<(), fern::InitError> { /// fern::Dispatch::new() /// .chain(std::fs::File::create("log")?) /// # .into_log(); /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// ```no_run /// # fn setup_logger() -> Result<(), fern::InitError> { /// fern::Dispatch::new() /// .chain(fern::log_file("log")?) /// # .into_log(); /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// Example usage (using [`fern::log_file`]): /// /// ```no_run /// # fn setup_logger() -> Result<(), fern::InitError> { /// fern::Dispatch::new() /// .chain(fern::Output::file(fern::log_file("log")?, "\r\n")) /// # .into_log(); /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// [`fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain /// [`fern::log_file`]: fn.log_file.html pub fn file>>(file: fs::File, line_sep: T) -> Self { Output(OutputInner::File { stream: file, line_sep: line_sep.into(), }) } /// Returns a logger using arbitrary write object and custom separator. /// /// If the default separator of `\n` is acceptable, an `Box` /// instance can be passed into [`Dispatch::chain`] directly. /// /// ```no_run /// # fn setup_logger() -> Result<(), fern::InitError> { /// // Anything implementing 'Write' works. /// let mut writer = std::io::Cursor::new(Vec::::new()); /// /// fern::Dispatch::new() /// // as long as we explicitly cast into a type-erased Box /// .chain(Box::new(writer) as Box) /// # .into_log(); /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// Example usage: /// /// ```no_run /// # fn setup_logger() -> Result<(), fern::InitError> { /// let writer = Box::new(std::io::Cursor::new(Vec::::new())); /// /// fern::Dispatch::new() /// .chain(fern::Output::writer(writer, "\r\n")) /// # .into_log(); /// # Ok(()) /// # } /// # /// # fn main() { setup_logger().expect("failed to set up logger"); } /// ``` /// /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain pub fn writer>>(writer: Box, line_sep: T) -> Self { Output(OutputInner::Writer { stream: writer, line_sep: line_sep.into(), }) } /// Returns an stdout logger using a custom separator. /// /// If the default separator of `\n` is acceptable, an `io::Stdout` /// instance can be passed into `Dispatch::chain()` directly. /// /// ``` /// fern::Dispatch::new() /// .chain(std::io::stdout()) /// # .into_log(); /// ``` /// /// Example usage: /// /// ``` /// fern::Dispatch::new() /// // some unix tools use null bytes as message terminators so /// // newlines in messages can be treated differently. /// .chain(fern::Output::stdout("\0")) /// # .into_log(); /// ``` pub fn stdout>>(line_sep: T) -> Self { Output(OutputInner::Stdout { stream: io::stdout(), line_sep: line_sep.into(), }) } /// Returns an stderr logger using a custom separator. /// /// If the default separator of `\n` is acceptable, an `io::Stderr` /// instance can be passed into `Dispatch::chain()` directly. /// /// ``` /// fern::Dispatch::new() /// .chain(std::io::stderr()) /// # .into_log(); /// ``` /// /// Example usage: /// /// ``` /// fern::Dispatch::new() /// .chain(fern::Output::stderr("\n\n\n")) /// # .into_log(); /// ``` pub fn stderr>>(line_sep: T) -> Self { Output(OutputInner::Stderr { stream: io::stderr(), line_sep: line_sep.into(), }) } /// Returns a mpsc::Sender logger using a custom separator. /// /// If the default separator of `\n` is acceptable, an /// `mpsc::Sender` instance can be passed into `Dispatch:: /// chain()` directly. /// /// Each log message will be suffixed with the separator, then sent as a /// single String to the given sender. /// /// ``` /// use std::sync::mpsc::channel; /// /// let (tx, rx) = channel(); /// fern::Dispatch::new() /// .chain(tx) /// # .into_log(); /// ``` pub fn sender>>(sender: Sender, line_sep: T) -> Self { Output(OutputInner::Sender { stream: sender, line_sep: line_sep.into(), }) } /// Returns a logger which logs into an RFC5424 syslog. /// /// This method takes an additional transform method to turn the log data /// into RFC5424 data. /// /// I've honestly got no clue what the expected keys and values are for /// this kind of logging, so I'm just going to link [the rfc] instead. /// /// If you're an expert on syslog logging and would like to contribute /// an example to put here, it would be gladly accepted! /// /// This requires the `"syslog-4"` feature. /// /// [the rfc]: https://tools.ietf.org/html/rfc5424 #[cfg(all(not(windows), feature = "syslog-4"))] pub fn syslog_5424(logger: Syslog4Rfc5424Logger, transform: F) -> Self where F: Fn(&log::Record) -> (i32, HashMap>, String) + Sync + Send + 'static, { Output(OutputInner::Syslog4Rfc5424 { logger, transform: Box::new(transform), }) } /// Returns a logger which simply calls the given function with each message. /// /// The function will be called inline in the thread the log occurs on. /// /// Example usage: /// /// ``` /// fern::Dispatch::new() /// .chain(fern::Output::call(|record| { /// // this is mundane, but you can do anything here. /// println!("{}", record.args()); /// })) /// # .into_log(); /// ``` pub fn call(func: F) -> Self where F: Fn(&log::Record) + Sync + Send + 'static, { struct CallShim(F); impl log::Log for CallShim where F: Fn(&log::Record) + Sync + Send + 'static, { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { (self.0)(record) } fn flush(&self) {} } Self::from(Box::new(CallShim(func)) as Box) } } impl Default for Dispatch { /// Returns a logger configuration that does nothing with log records. /// /// Equivalent to [`Dispatch::new`]. /// /// [`Dispatch::new`]: #method.new fn default() -> Self { Self::new() } } impl fmt::Debug for Dispatch { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { struct LevelsDebug<'a>(&'a [(Cow<'static, str>, log::LevelFilter)]); impl<'a> fmt::Debug for LevelsDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_map() .entries(self.0.iter().map(|t| (t.0.as_ref(), t.1))) .finish() } } struct FiltersDebug<'a>(&'a [Box]); impl<'a> fmt::Debug for FiltersDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_list() .entries(self.0.iter().map(|_| "")) .finish() } } f.debug_struct("Dispatch") .field( "format", &self.format.as_ref().map(|_| ""), ) .field("children", &self.children) .field("default_level", &self.default_level) .field("levels", &LevelsDebug(&self.levels)) .field("filters", &FiltersDebug(&self.filters)) .finish() } } impl fmt::Debug for OutputInner { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { OutputInner::Stdout { ref stream, ref line_sep, } => f .debug_struct("Output::Stdout") .field("stream", stream) .field("line_sep", line_sep) .finish(), OutputInner::Stderr { ref stream, ref line_sep, } => f .debug_struct("Output::Stderr") .field("stream", stream) .field("line_sep", line_sep) .finish(), OutputInner::File { ref stream, ref line_sep, } => f .debug_struct("Output::File") .field("stream", stream) .field("line_sep", line_sep) .finish(), OutputInner::Writer { ref line_sep, .. } => f .debug_struct("Output::Writer") .field("stream", &"") .field("line_sep", line_sep) .finish(), OutputInner::Sender { ref stream, ref line_sep, } => f .debug_struct("Output::Sender") .field("stream", stream) .field("line_sep", line_sep) .finish(), #[cfg(all(not(windows), feature = "syslog-3"))] OutputInner::Syslog3(_) => f .debug_tuple("Output::Syslog3") .field(&"") .finish(), #[cfg(all(not(windows), feature = "syslog-4"))] OutputInner::Syslog4Rfc3164 { .. } => f .debug_tuple("Output::Syslog4Rfc3164") .field(&"") .finish(), #[cfg(all(not(windows), feature = "syslog-4"))] OutputInner::Syslog4Rfc5424 { .. } => f .debug_tuple("Output::Syslog4Rfc5424") .field(&"") .finish(), OutputInner::Dispatch(ref dispatch) => { f.debug_tuple("Output::Dispatch").field(dispatch).finish() } OutputInner::SharedDispatch(_) => f .debug_tuple("Output::SharedDispatch") .field(&"") .finish(), OutputInner::OtherBoxed { .. } => f .debug_tuple("Output::OtherBoxed") .field(&"") .finish(), OutputInner::OtherStatic { .. } => f .debug_tuple("Output::OtherStatic") .field(&"") .finish(), OutputInner::Panic => f.debug_tuple("Output::Panic").finish(), } } } impl fmt::Debug for Output { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } fern-0.5.8/src/colors.rs010064400017500000144000000243201331512532100133260ustar0000000000000000//! Support for ANSI terminal colors via the colored crate. //! //! To enable support for colors, add the `"colored"` feature in your //! `Cargo.toml`: //! //! ```toml //! [dependencies] //! fern = { version = "0.5", features = ["colored"] } //! ``` //! //! --- //! //! Colors are mainly supported via coloring the log level itself, but it's //! also possible to color each entire log line based off of the log level. //! //! First, here's an example which colors the log levels themselves ("INFO" / //! "WARN" text will have color, but won't color the rest of the line). //! [`ColoredLevelConfig`] lets us configure the colors per-level, but also has //! sane defaults. //! //! ``` //! use fern::colors::{Color, ColoredLevelConfig}; //! //! let mut colors = ColoredLevelConfig::new() //! // use builder methods //! .info(Color::Green); //! // or access raw fields //! colors.warn = Color::Magenta; //! ``` //! //! It can then be used within any regular fern formatting closure: //! //! ``` //! # let colors = fern::colors::ColoredLevelConfig::new(); //! fern::Dispatch::new() //! // ... //! .format(move |out, message, record| { //! out.finish(format_args!( //! "[{}] {}", //! // just use 'colors.color(..)' instead of the level //! // itself to insert ANSI colors. //! colors.color(record.level()), //! message, //! )) //! }) //! # .into_log(); //! ``` //! //! --- //! //! Coloring levels is nice, but the alternative is good to. For an example //! application coloring each entire log line with the right color, see //! [examples/pretty-colored.rs][ex]. //! //! [`ColoredLevelConfig`]: struct.ColoredLevelConfig.html //! [ex]: https://github.com/daboross/fern/blob/master/examples/pretty-colored.rs use std::fmt; pub use colored::Color; use log::Level; /// Extension crate allowing the use of `.colored` on Levels. trait ColoredLogLevel { /// Colors this log level with the given color. fn colored(&self, color: Color) -> WithFgColor; } /// Opaque structure which represents some text data and a color to display it /// with. /// /// This implements [`fmt::Display`] to displaying the inner text (usually a /// log level) with ANSI color markers before to set the color and after to /// reset the color. /// /// `WithFgColor` instances can be created and displayed without any allocation. // this is necessary in order to avoid using colored::ColorString, which has a // Display implementation involving many allocations, and would involve two // more string allocations even to create it. // // [`fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html pub struct WithFgColor where T: fmt::Display, { text: T, color: Color, } impl fmt::Display for WithFgColor where T: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{}m", self.color.to_fg_str())?; fmt::Display::fmt(&self.text, f)?; write!(f, "\x1B[0m")?; Ok(()) } } /// Configuration specifying colors a log level can be colored as. /// /// Example usage setting custom 'info' and 'debug' colors: /// /// ``` /// use fern::colors::{Color, ColoredLevelConfig}; /// /// let colors = ColoredLevelConfig::new() /// .info(Color::Green) /// .debug(Color::Magenta); /// /// fern::Dispatch::new() /// .format(move |out, message, record| { /// out.finish(format_args!( /// "[{}] {}", /// colors.color(record.level()), /// message /// )) /// }) /// .chain(std::io::stdout()) /// # /* /// .apply()?; /// # */ /// # .into_log(); /// ``` #[derive(Copy, Clone)] #[must_use = "builder methods take config by value and thus must be reassigned to variable"] pub struct ColoredLevelConfig { /// The color to color logs with the [`Error`] level. /// /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error pub error: Color, /// The color to color logs with the [`Warn`] level. /// /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn pub warn: Color, /// The color to color logs with the [`Info`] level. /// /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info pub info: Color, /// The color to color logs with the [`Debug`] level. /// /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug pub debug: Color, /// The color to color logs with the [`Trace`] level. /// /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace pub trace: Color, } impl ColoredLevelConfig { /// Creates a new ColoredLevelConfig with the default colors. /// /// This matches the behavior of [`ColoredLevelConfig::default`]. /// /// [`ColoredLevelConfig::default`]: #method.default #[inline] pub fn new() -> Self { Self::default() } /// Overrides the [`Error`] level color with the given color. /// /// The default color is [`Color::Red`]. /// /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red pub fn error(mut self, error: Color) -> Self { self.error = error; self } /// Overrides the [`Warn`] level color with the given color. /// /// The default color is [`Color::Yellow`]. /// /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow pub fn warn(mut self, warn: Color) -> Self { self.warn = warn; self } /// Overrides the [`Info`] level color with the given color. /// /// The default color is [`Color::White`]. /// /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White pub fn info(mut self, info: Color) -> Self { self.info = info; self } /// Overrides the [`Debug`] level color with the given color. /// /// The default color is [`Color::White`]. /// /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White pub fn debug(mut self, debug: Color) -> Self { self.debug = debug; self } /// Overrides the [`Trace`] level color with the given color. /// /// The default color is [`Color::White`]. /// /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White pub fn trace(mut self, trace: Color) -> Self { self.trace = trace; self } /// Colors the given log level with the color in this configuration /// corresponding to it's level. /// /// The structure returned is opaque, but will print the Level surrounded /// by ANSI color codes when displayed. This will work correctly for /// UNIX terminals, but due to a lack of support from the [`colored`] /// crate, this will not function in Windows. /// /// [`colored`]: https://github.com/mackwic/colored pub fn color(&self, level: Level) -> WithFgColor { level.colored(self.get_color(&level)) } /// Retrieves the color that a log level should be colored as. pub fn get_color(&self, level: &Level) -> Color { match *level { Level::Error => self.error, Level::Warn => self.warn, Level::Info => self.info, Level::Debug => self.debug, Level::Trace => self.trace, } } } impl Default for ColoredLevelConfig { /// Retrieves the default configuration. This has: /// /// - [`Error`] as [`Color::Red`] /// - [`Warn`] as [`Color::Yellow`] /// - [`Info`] as [`Color::White`] /// - [`Debug`] as [`Color::White`] /// - [`Trace`] as [`Color::White`] /// /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red fn default() -> Self { ColoredLevelConfig { error: Color::Red, warn: Color::Yellow, debug: Color::White, info: Color::White, trace: Color::White, } } } impl ColoredLogLevel for Level { fn colored(&self, color: Color) -> WithFgColor { WithFgColor { text: *self, color: color, } } } #[cfg(test)] mod test { use colored::Color::*; use colored::Colorize; use super::WithFgColor; #[test] fn fg_color_matches_colored_behavior() { for &color in &[ Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, BrightBlack, BrightRed, BrightGreen, BrightYellow, BrightBlue, BrightMagenta, BrightCyan, BrightWhite, ] { assert_eq!( format!("{}", "test".color(color)), format!( "{}", WithFgColor { text: "test", color: color, } ) ); } } #[test] fn fg_color_respects_formatting_flags() { let s = format!( "{:^8}", WithFgColor { text: "test", color: Yellow, } ); assert!(s.contains(" test ")); assert!(!s.contains(" test ")); assert!(!s.contains(" test ")); } } fern-0.5.8/src/errors.rs010064400017500000144000000034201330631727500133520ustar0000000000000000use std::{error, fmt, io}; use log; /// Convenience error combining possible errors which could occur while /// initializing logging. /// /// Fern does not use this error natively, but functions which set up fern and /// open log files will often need to return both [`io::Error`] and /// [`SetLoggerError`]. This error is for that purpose. /// /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html /// [`SetLoggerError`]: ../log/struct.SetLoggerError.html #[derive(Debug)] pub enum InitError { /// IO error. Io(io::Error), /// The log crate's global logger was already initialized when trying to /// initialize a logger. SetLoggerError(log::SetLoggerError), } impl From for InitError { fn from(error: io::Error) -> InitError { InitError::Io(error) } } impl From for InitError { fn from(error: log::SetLoggerError) -> InitError { InitError::SetLoggerError(error) } } impl fmt::Display for InitError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { InitError::Io(ref e) => write!(f, "IO Error initializing logger: {}", e), InitError::SetLoggerError(ref e) => write!(f, "logging initialization failed: {}", e), } } } impl error::Error for InitError { fn description(&self) -> &str { match *self { InitError::Io(..) => "IO error while initializing logging", InitError::SetLoggerError(..) => { "logging system already initialized with different logger" } } } fn cause(&self) -> Option<&error::Error> { match *self { InitError::Io(ref e) => Some(e), InitError::SetLoggerError(ref e) => Some(e), } } } fern-0.5.8/src/lib.rs010064400017500000144000000211371344633166600126160ustar0000000000000000#![deny(missing_docs)] #![doc(html_root_url = "https://docs.rs/fern/0.5.8")] //! Efficient, configurable logging in Rust. //! //! # Depending on fern //! //! Ensure you require both fern and log in your project's `Cargo.toml`: //! //! ```toml //! [dependencies] //! log = "0.4" //! fern = "0.5" //! ``` //! //! Then declare both in `main.rs` or `lib.rs`: //! //! ``` //! #[macro_use] //! extern crate log; //! extern crate fern; //! # fn main() {} //! ``` //! //! # Example setup //! //! With fern, all logger configuration is done via builder-like methods on //! instances of the [`Dispatch`] structure. //! //! Here's an example logger which formats messages, and sends everything Debug //! and above to both stdout and an output.log file: //! //! ```no_run //! extern crate fern; //! #[macro_use] //! extern crate log; //! //! extern crate chrono; //! //! fn setup_logger() -> Result<(), fern::InitError> { //! fern::Dispatch::new() //! .format(|out, message, record| { //! out.finish(format_args!( //! "{}[{}][{}] {}", //! chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), //! record.target(), //! record.level(), //! message //! )) //! }) //! .level(log::LevelFilter::Debug) //! .chain(std::io::stdout()) //! .chain(fern::log_file("output.log")?) //! .apply()?; //! Ok(()) //! } //! # fn main() { //! # setup_logger().expect("failed to set up logger") //! # } //! ``` //! //! Let's unwrap this: //! //! --- //! //! [`fern::Dispatch::new()`] //! //! Create an empty configuration. //! //! --- //! //! [`.format(|...| ...)`] //! //! Add a formatter to the logger, modifying all messages sent through. //! //! ___ //! //! [`chrono::Local::now()`] //! //! Get the current time in the local timezone using the [`chrono`] library. //! See the [time-and-date docs]. //! //! ___ //! //! [`.format("[%Y-%m-%d][%H:%M:%S]")`][chrono-format] //! //! Use chrono's lazy format specifier to turn the time into a readable string. //! //! --- //! //! [`out.finish(format_args!(...))`] //! //! Call the `fern::FormattingCallback` to submit the formatted message. //! //! This roundabout way is slightly odd, but it allows for very fast logging. //! No string allocation required! //! //! [`format_args!()`] has the same format as [`println!()`] \(and every other //! [`std::fmt`]-based macro). //! //! --- //! //! [`.level(log::LevelFilter::Debug)`] //! //! Set the minimum level needed to output to `Debug`. //! //! --- //! //! [`.chain(std::io::stdout())`] //! //! Add a child to the logger. All messages which pass the filters will be sent //! to stdout. //! //! [`Dispatch::chain`] accepts [`Stdout`], [`Stderr`], [`File`] and other //! [`Dispatch`] instances. //! //! --- //! //! [`.chain(fern::log_file(...)?)`] //! //! Add a second child sending messages to the file "output.log". //! //! See [`fern::log_file()`] for more info on file output. //! //! --- //! //! [`.apply()`][`.apply`] //! //! Consume the configuration and instantiate it as the current runtime global //! logger. //! //! This will fail if and only if `.apply()` or equivalent form another crate //! has already been used this runtime. //! //! Since the binary crate is the only one ever setting up logging, the //! [`apply`] result can be reasonably unwrapped: it's a bug if any crate is //! calling this method more than once. //! //! --- //! //! The final output will look like: //! //! ```text //! [2017-01-20][12:55:04][crate-name][INFO] Hello, world! //! [2017-01-20][12:56:21][crate-name][WARN] Ahhh! //! [2017-01-20][12:58:00][crate-name][DEBUG] Something less important happened. //! ``` //! //! # Logging //! //! Once the logger has been set, it will pick up all logging calls from your //! crate and all libraries you depend on. //! //! ```rust //! # #[macro_use] //! # extern crate log; //! # extern crate fern; //! //! # fn setup_logger() -> Result<(), fern::InitError> { //! fern::Dispatch::new() //! // ... //! .apply()?; //! # Ok(()) //! # } //! //! # fn main() { //! # setup_logger().ok(); // we're ok with this not succeeding. //! trace!("Trace message"); //! debug!("Debug message"); //! info!("Info message"); //! warn!("Warning message"); //! error!("Error message"); //! # } //! ``` //! //! # More //! //! The [`Dispatch` documentation] has example usages of each method, and the //! [full example program] might be useful for using fern in a larger //! application context. //! //! See the [colors] module for examples using ANSI terminal coloring. //! //! See the [syslog] module for examples outputting to the unix syslog, or the //! [syslog full example program] for a more realistic sample. //! //! See the [meta] module for information on getting logging-within-logging //! working correctly. //! //! [`fern::Dispatch::new()`]: struct.Dispatch.html#method.new //! [`.format(|...| ...)`]: struct.Dispatch.html#method.format //! [`chrono::Local::now()`]: https://docs.rs/chrono/0.4/chrono/offset/local/struct.Local.html#method.now //! [chrono-format]: https://docs.rs/chrono/0.4/chrono/datetime/struct.DateTime.html#method.format //! [`out.finish(format_args!(...))`]: struct.FormatCallback.html#method.finish //! [`.level(log::LevelFilter::Debug)`]: struct.Dispatch.html#method.level //! [`Dispatch::chain`]: struct.Dispatch.html#method.chain //! [`.chain(std::io::stdout())`]: struct.Dispatch.html#method.chain //! [`Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html //! [`Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html //! [`File`]: https://doc.rust-lang.org/std/fs/struct.File.html //! [`Dispatch`]: struct.Dispatch.html //! [`.chain(fern::log_file(...)?)`]: struct.Dispatch.html#method.chain //! [`fern::log_file()`]: fn.log_file.html //! [`.apply`]: struct.Dispatch.html#method.apply //! [`format_args!()`]: https://doc.rust-lang.org/std/macro.format_args.html //! [`println!()`]: https://doc.rust-lang.org/std/macro.println.html //! [`std::fmt`]: https://doc.rust-lang.org/std/fmt/ //! [`chrono`]: https://github.com/chronotope/chrono //! [time-and-date docs]: https://docs.rs/chrono/0.4/chrono/index.html#date-and-time //! [the format specifier docs]: https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers //! [`Dispatch` documentation]: struct.Dispatch.html //! [full example program]: https://github.com/daboross/fern/tree/master/examples/cmd-program.rs //! [syslog full example program]: https://github.com/daboross/fern/tree/master/examples/syslog.rs //! [`apply`]: struct.Dispatch.html#method.apply //! [colors]: colors/index.html //! [syslog]: syslog/index.html //! [meta]: meta/index.html #[cfg(feature = "colored")] extern crate colored; extern crate log; #[cfg(all(not(windows), feature = "syslog-4"))] extern crate syslog as syslog_4; #[cfg(all(not(windows), feature = "syslog-3"))] extern crate syslog3 as syslog_3; use std::convert::AsRef; use std::fs::{File, OpenOptions}; use std::path::Path; use std::{fmt, io}; #[cfg(all(not(windows), feature = "syslog-4"))] use std::collections::HashMap; pub use builders::{Dispatch, Output, Panic}; pub use errors::InitError; pub use log_impl::FormatCallback; mod builders; mod errors; mod log_impl; #[cfg(feature = "colored")] pub mod colors; #[cfg(all(not(windows), feature = "syslog-3", feature = "syslog-4"))] pub mod syslog; pub mod meta; /// A type alias for a log formatter. /// /// As of fern `0.5`, the passed `fmt::Arguments` will always be the same as /// the given `log::Record`'s `.args()`. pub type Formatter = Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static; /// A type alias for a log filter. Returning true means the record should /// succeed - false means it should fail. pub type Filter = Fn(&log::Metadata) -> bool + Send + Sync + 'static; #[cfg(all(not(windows), feature = "syslog-4"))] type Syslog4Rfc3164Logger = syslog_4::Logger; #[cfg(all(not(windows), feature = "syslog-4"))] type Syslog4Rfc5424Logger = syslog_4::Logger< syslog_4::LoggerBackend, (i32, HashMap>, String), syslog_4::Formatter5424, >; /// Convenience method for opening a log file with common options. /// /// Equivalent to: /// /// ```no_run /// std::fs::OpenOptions::new() /// .write(true) /// .create(true) /// .append(true) /// .open("filename") /// # ; /// ``` /// /// See [`OpenOptions`] for more information. /// /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html #[inline] pub fn log_file>(path: P) -> io::Result { OpenOptions::new() .write(true) .create(true) .append(true) .open(path) } fern-0.5.8/src/log_impl.rs010064400017500000144000000601131344633302200136330ustar0000000000000000use std::borrow::Cow; use std::io::{self, BufWriter, Write}; use std::sync::mpsc; use std::sync::{Arc, Mutex}; use std::{fmt, fs}; use std::collections::HashMap; use log::{self, Log}; use {Filter, Formatter}; #[cfg(all(not(windows), feature = "syslog-3"))] use syslog_3; #[cfg(all(not(windows), feature = "syslog-4"))] use {syslog_4, Syslog4Rfc3164Logger, Syslog4Rfc5424Logger}; pub enum LevelConfiguration { JustDefault, Minimal(Vec<(Cow<'static, str>, log::LevelFilter)>), Many(HashMap, log::LevelFilter>), } pub struct Dispatch { pub output: Vec, pub default_level: log::LevelFilter, pub levels: LevelConfiguration, pub format: Option>, pub filters: Vec>, } /// Callback struct for use within a formatter closure /// /// Callbacks are used for formatting in order to allow usage of /// [`std::fmt`]-based formatting without the allocation of the formatted /// result which would be required to return it. /// /// Example usage: /// /// ``` /// fern::Dispatch::new().format(|callback: fern::FormatCallback, message, record| { /// callback.finish(format_args!("[{}] {}", record.level(), message)) /// }) /// # ; /// ``` /// /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html #[must_use = "format callback must be used for log to process correctly"] pub struct FormatCallback<'a>(InnerFormatCallback<'a>); struct InnerFormatCallback<'a>(&'a mut bool, &'a Dispatch, &'a log::Record<'a>); pub enum Output { Stdout(Stdout), Stderr(Stderr), File(File), Sender(Sender), #[cfg(all(not(windows), feature = "syslog-3"))] Syslog3(Syslog3), #[cfg(all(not(windows), feature = "syslog-4"))] Syslog4Rfc3164(Syslog4Rfc3164), #[cfg(all(not(windows), feature = "syslog-4"))] Syslog4Rfc5424(Syslog4Rfc5424), Dispatch(Dispatch), SharedDispatch(Arc), OtherBoxed(Box), OtherStatic(&'static Log), Panic(Panic), Writer(Writer), } pub struct Stdout { pub stream: io::Stdout, pub line_sep: Cow<'static, str>, } pub struct Stderr { pub stream: io::Stderr, pub line_sep: Cow<'static, str>, } pub struct File { pub stream: Mutex>, pub line_sep: Cow<'static, str>, } pub struct Sender { pub stream: Mutex>, pub line_sep: Cow<'static, str>, } pub struct Writer { pub stream: Mutex>, pub line_sep: Cow<'static, str>, } #[cfg(all(not(windows), feature = "syslog-3"))] pub struct Syslog3 { pub inner: syslog_3::Logger, } #[cfg(all(not(windows), feature = "syslog-4"))] pub struct Syslog4Rfc3164 { pub inner: Mutex, } #[cfg(all(not(windows), feature = "syslog-4"))] pub struct Syslog4Rfc5424 { pub inner: Mutex, pub transform: Box< Fn(&log::Record) -> (i32, HashMap>, String) + Sync + Send, >, } pub struct Panic; pub struct Null; impl From, log::LevelFilter)>> for LevelConfiguration { fn from(mut levels: Vec<(Cow<'static, str>, log::LevelFilter)>) -> Self { // Benchmarked separately: https://gist.github.com/daboross/976978d8200caf86e02acb6805961195 // Use Vec if there are fewer than 15 items, HashMap if there are more than 15. match levels.len() { 0 => LevelConfiguration::JustDefault, x if x > 15 => LevelConfiguration::Many(levels.into_iter().collect()), _ => { levels.shrink_to_fit(); LevelConfiguration::Minimal(levels) } } } } impl LevelConfiguration { // inline since we use it literally once. #[inline] fn find_module(&self, module: &str) -> Option { match *self { LevelConfiguration::JustDefault => None, _ => { if let Some(level) = self.find_exact(module) { return Some(level); } // The manual for loop here lets us just iterate over the module string once // while still finding each sub-module. For the module string // "hyper::http::h1", this loop will test first "hyper::http" // then "hyper". let mut last_char_colon = false; for (index, ch) in module.char_indices().rev() { if last_char_colon { last_char_colon = false; if ch == ':' { let sub_module = &module[0..index]; if let Some(level) = self.find_exact(sub_module) { return Some(level); } } } else if ch == ':' { last_char_colon = true; } } None } } } fn find_exact(&self, module: &str) -> Option { match *self { LevelConfiguration::JustDefault => None, LevelConfiguration::Minimal(ref levels) => levels .iter() .find(|&&(ref test_module, _)| test_module == module) .map(|&(_, level)| level), LevelConfiguration::Many(ref levels) => levels.get(module).cloned(), } } } impl Log for Output { fn enabled(&self, metadata: &log::Metadata) -> bool { match *self { Output::Stdout(ref s) => s.enabled(metadata), Output::Stderr(ref s) => s.enabled(metadata), Output::File(ref s) => s.enabled(metadata), Output::Sender(ref s) => s.enabled(metadata), Output::Dispatch(ref s) => s.enabled(metadata), Output::SharedDispatch(ref s) => s.enabled(metadata), Output::OtherBoxed(ref s) => s.enabled(metadata), Output::OtherStatic(ref s) => s.enabled(metadata), #[cfg(all(not(windows), feature = "syslog-3"))] Output::Syslog3(ref s) => s.enabled(metadata), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc3164(ref s) => s.enabled(metadata), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc5424(ref s) => s.enabled(metadata), Output::Panic(ref s) => s.enabled(metadata), Output::Writer(ref s) => s.enabled(metadata), } } fn log(&self, record: &log::Record) { match *self { Output::Stdout(ref s) => s.log(record), Output::Stderr(ref s) => s.log(record), Output::File(ref s) => s.log(record), Output::Sender(ref s) => s.log(record), Output::Dispatch(ref s) => s.log(record), Output::SharedDispatch(ref s) => s.log(record), Output::OtherBoxed(ref s) => s.log(record), Output::OtherStatic(ref s) => s.log(record), #[cfg(all(not(windows), feature = "syslog-3"))] Output::Syslog3(ref s) => s.log(record), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc3164(ref s) => s.log(record), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc5424(ref s) => s.log(record), Output::Panic(ref s) => s.log(record), Output::Writer(ref s) => s.log(record), } } fn flush(&self) { match *self { Output::Stdout(ref s) => s.flush(), Output::Stderr(ref s) => s.flush(), Output::File(ref s) => s.flush(), Output::Sender(ref s) => s.flush(), Output::Dispatch(ref s) => s.flush(), Output::SharedDispatch(ref s) => s.flush(), Output::OtherBoxed(ref s) => s.flush(), Output::OtherStatic(ref s) => s.flush(), #[cfg(all(not(windows), feature = "syslog-3"))] Output::Syslog3(ref s) => s.flush(), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc3164(ref s) => s.flush(), #[cfg(all(not(windows), feature = "syslog-4"))] Output::Syslog4Rfc5424(ref s) => s.flush(), Output::Panic(ref s) => s.flush(), Output::Writer(ref s) => s.flush(), } } } impl Log for Null { fn enabled(&self, _: &log::Metadata) -> bool { false } fn log(&self, _: &log::Record) {} fn flush(&self) {} } impl Log for Dispatch { fn enabled(&self, metadata: &log::Metadata) -> bool { self.deep_enabled(metadata) } fn log(&self, record: &log::Record) { if self.shallow_enabled(record.metadata()) { match self.format { Some(ref format) => { // flag to ensure the log message is completed even if the formatter doesn't // complete the callback. let mut callback_called_flag = false; (format)( FormatCallback(InnerFormatCallback( &mut callback_called_flag, self, record, )), record.args(), record, ); if !callback_called_flag { self.finish_logging(record); } } None => { self.finish_logging(record); } } } } fn flush(&self) { for log in &self.output { log.flush(); } } } impl Dispatch { fn finish_logging(&self, record: &log::Record) { for log in &self.output { log.log(record); } } /// Check whether this log's filters prevent the given log from happening. fn shallow_enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= self .levels .find_module(metadata.target()) .unwrap_or(self.default_level) && self.filters.iter().all(|f| f(metadata)) } /// Check whether a log with the given metadata would eventually end up outputting something. /// /// This is recursive, and checks children. fn deep_enabled(&self, metadata: &log::Metadata) -> bool { self.shallow_enabled(metadata) && self.output.iter().any(|l| l.enabled(metadata)) } } impl<'a> FormatCallback<'a> { /// Complete the formatting call that this FormatCallback was created for. /// /// This will call the rest of the logging chain using the given formatted /// message as the new payload message. /// /// Example usage: /// /// ``` /// # fern::Dispatch::new() /// # .format(|callback: fern::FormatCallback, message, record| { /// callback.finish(format_args!("[{}] {}", record.level(), message)) /// # }) /// # .into_log(); /// ``` /// /// See [`format_args!`]. /// /// [`format_args!`]: https://doc.rust-lang.org/std/macro.format_args.html pub fn finish(self, formatted_message: fmt::Arguments) { let FormatCallback(InnerFormatCallback(callback_called_flag, dispatch, record)) = self; // let the dispatch know that we did in fact get called. *callback_called_flag = true; // NOTE: This needs to be updated whenever new things are added to // `log::Record`. let new_record = log::RecordBuilder::new() .args(formatted_message) .metadata(record.metadata().clone()) .level(record.level()) .target(record.target()) .module_path(record.module_path()) .file(record.file()) .line(record.line()) .build(); dispatch.finish_logging(&new_record); } } // No need to write this twice (used for Stdout and Stderr structs) macro_rules! std_log_impl { ($ident:ident) => { impl Log for $ident { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { if cfg!(feature = "meta-logging-in-format") { // Formatting first prevents deadlocks when the process of formatting // itself is logged. note: this is only ever needed if some // Debug, Display, or other formatting trait itself is // logging things too. let msg = format!("{}{}", record.args(), self.line_sep); write!(self.stream.lock(), "{}", msg)?; } else { write!(self.stream.lock(), "{}{}", record.args(), self.line_sep)?; } Ok(()) }); } fn flush(&self) { let _ = self.stream.lock().flush(); } } }; } std_log_impl!(Stdout); std_log_impl!(Stderr); macro_rules! writer_log_impl { ($ident:ident) => { impl Log for $ident { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { if cfg!(feature = "meta-logging-in-format") { // Formatting first prevents deadlocks on file-logging, // when the process of formatting itself is logged. // note: this is only ever needed if some Debug, Display, or other // formatting trait itself is logging. let msg = format!("{}{}", record.args(), self.line_sep); let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner()); write!(writer, "{}", msg)?; writer.flush()?; } else { let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner()); write!(writer, "{}{}", record.args(), self.line_sep)?; writer.flush()?; } Ok(()) }); } fn flush(&self) { let _ = self .stream .lock() .unwrap_or_else(|e| e.into_inner()) .flush(); } } }; } writer_log_impl!(File); writer_log_impl!(Writer); impl Log for Sender { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { let msg = format!("{}{}", record.args(), self.line_sep); self.stream .lock() .unwrap_or_else(|e| e.into_inner()) .send(msg)?; Ok(()) }); } fn flush(&self) {} } #[cfg(any(feature = "syslog-3", feature = "syslog-4"))] macro_rules! send_syslog { ($logger:expr, $level:expr, $message:expr) => { use log::Level; match $level { Level::Error => $logger.err($message)?, Level::Warn => $logger.warning($message)?, Level::Info => $logger.info($message)?, Level::Debug | Level::Trace => $logger.debug($message)?, } }; } #[cfg(all(not(windows), feature = "syslog-3"))] impl Log for Syslog3 { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { let message = record.args(); send_syslog!(self.inner, record.level(), message); Ok(()) }); } fn flush(&self) {} } #[cfg(all(not(windows), feature = "syslog-4"))] impl Log for Syslog4Rfc3164 { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { let message = record.args().to_string(); let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner()); send_syslog!(log, record.level(), message); Ok(()) }); } fn flush(&self) {} } #[cfg(all(not(windows), feature = "syslog-4"))] impl Log for Syslog4Rfc5424 { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { fallback_on_error(record, |record| { let transformed = (self.transform)(record); let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner()); send_syslog!(log, record.level(), transformed); Ok(()) }); } fn flush(&self) {} } impl Log for Panic { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { panic!("{}", record.args()); } fn flush(&self) {} } #[inline(always)] fn fallback_on_error(record: &log::Record, log_func: F) where F: FnOnce(&log::Record) -> Result<(), LogError>, { if let Err(error) = log_func(record) { backup_logging(record, &error) } } fn backup_logging(record: &log::Record, error: &LogError) { let second = write!( io::stderr(), "Error performing logging.\ \n\tattempted to log: {}\ \n\trecord: {:?}\ \n\tlogging error: {}", record.args(), record, error ); if let Err(second_error) = second { panic!( "Error performing stderr logging after error occurred during regular logging.\ \n\tattempted to log: {}\ \n\trecord: {:?}\ \n\tfirst logging error: {}\ \n\tstderr error: {}", record.args(), record, error, second_error, ); } } #[derive(Debug)] enum LogError { Io(io::Error), Send(mpsc::SendError), #[cfg(all(not(windows), feature = "syslog-4"))] Syslog4(syslog_4::Error), } impl fmt::Display for LogError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { LogError::Io(ref e) => write!(f, "{}", e), LogError::Send(ref e) => write!(f, "{}", e), #[cfg(all(not(windows), feature = "syslog-4"))] LogError::Syslog4(ref e) => write!(f, "{}", e), } } } impl From for LogError { fn from(error: io::Error) -> Self { LogError::Io(error) } } impl From> for LogError { fn from(error: mpsc::SendError) -> Self { LogError::Send(error) } } #[cfg(all(not(windows), feature = "syslog-4"))] impl From for LogError { fn from(error: syslog_4::Error) -> Self { LogError::Syslog4(error) } } #[cfg(test)] mod test { use super::LevelConfiguration; use log::LevelFilter::*; #[test] fn test_level_config_find_exact_minimal() { let config = LevelConfiguration::Minimal( vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)] .into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_exact("mod1"), Some(Info)); assert_eq!(config.find_exact("mod2"), Some(Debug)); assert_eq!(config.find_exact("mod3"), Some(Off)); } #[test] fn test_level_config_find_exact_many() { let config = LevelConfiguration::Many( vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)] .into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_exact("mod1"), Some(Info)); assert_eq!(config.find_exact("mod2"), Some(Debug)); assert_eq!(config.find_exact("mod3"), Some(Off)); } #[test] fn test_level_config_simple_hierarchy() { let config = LevelConfiguration::Minimal( vec![("mod1", Info), ("mod2::sub_mod", Debug), ("mod3", Off)] .into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_module("mod1::sub_mod"), Some(Info)); assert_eq!(config.find_module("mod2::sub_mod::sub_mod_2"), Some(Debug)); assert_eq!(config.find_module("mod3::sub_mod::sub_mod_2"), Some(Off)); } #[test] fn test_level_config_hierarchy_correct() { let config = LevelConfiguration::Minimal( vec![ ("root", Trace), ("root::sub1", Debug), ("root::sub2", Info), // should work with all insertion orders ("root::sub2::sub2.3::sub2.4", Error), ("root::sub2::sub2.3", Warn), ("root::sub3", Off), ].into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_module("root"), Some(Trace)); assert_eq!(config.find_module("root::other_module"), Some(Trace)); // We want to ensure that it does pick up most specific level before trying // anything more general. assert_eq!(config.find_module("root::sub1"), Some(Debug)); assert_eq!(config.find_module("root::sub1::other_module"), Some(Debug)); assert_eq!(config.find_module("root::sub2"), Some(Info)); assert_eq!(config.find_module("root::sub2::other"), Some(Info)); assert_eq!(config.find_module("root::sub2::sub2.3"), Some(Warn)); assert_eq!( config.find_module("root::sub2::sub2.3::sub2.4"), Some(Error) ); assert_eq!(config.find_module("root::sub3"), Some(Off)); assert_eq!( config.find_module("root::sub3::any::children::of::sub3"), Some(Off) ); } #[test] fn test_level_config_similar_names_are_not_same() { let config = LevelConfiguration::Minimal( vec![("root", Trace), ("rootay", Info)] .into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_module("root"), Some(Trace)); assert_eq!(config.find_module("root::sub"), Some(Trace)); assert_eq!(config.find_module("rooty"), None); assert_eq!(config.find_module("rooty::sub"), None); assert_eq!(config.find_module("rootay"), Some(Info)); assert_eq!(config.find_module("rootay::sub"), Some(Info)); } #[test] fn test_level_config_single_colon_is_not_double_colon() { let config = LevelConfiguration::Minimal( vec![ ("root", Trace), ("root::su", Debug), ("root::su:b2", Info), ("root::sub2", Warn), ].into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_module("root"), Some(Trace)); assert_eq!(config.find_module("root::su"), Some(Debug)); assert_eq!(config.find_module("root::su::b2"), Some(Debug)); assert_eq!(config.find_module("root::su:b2"), Some(Info)); assert_eq!(config.find_module("root::su:b2::b3"), Some(Info)); assert_eq!(config.find_module("root::sub2"), Some(Warn)); assert_eq!(config.find_module("root::sub2::b3"), Some(Warn)); } #[test] fn test_level_config_all_chars() { let config = LevelConfiguration::Minimal( vec![ ("♲", Trace), ("☸", Debug), ("♲::☸", Info), ("♲::\t", Debug), ].into_iter() .map(|(k, v)| (k.into(), v)) .collect(), ); assert_eq!(config.find_module("♲"), Some(Trace)); assert_eq!(config.find_module("♲::other"), Some(Trace)); assert_eq!(config.find_module("☸"), Some(Debug)); assert_eq!(config.find_module("☸::any"), Some(Debug)); assert_eq!(config.find_module("♲::☸"), Some(Info)); assert_eq!(config.find_module("♲☸"), None); assert_eq!(config.find_module("♲::\t"), Some(Debug)); assert_eq!(config.find_module("♲::\t::\n\n::\t"), Some(Debug)); assert_eq!(config.find_module("♲::\t\t"), Some(Trace)); } } fern-0.5.8/src/meta.rs010064400017500000144000000054211344633302200127600ustar0000000000000000/*! Fern supports logging most things by default, except for one kind of struct: structs which make log calls to the global logger from within their `Display` or `Debug` implementations. Here's an example of such a structure: ``` # #[macro_use] # extern crate log; # use std::fmt; # struct Thing<'a>(&'a str); impl<'a> fmt::Display for Thing<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { debug!("just displayed a Thing wrapping {}", self.0); f.write_str(self.0) } } # fn main() {} ``` This structure, and this structure alone, will cause some problems when logging in fern. There are mitigations, but since it's a fairly niche use case, they are disabled by default. The problems are, depending on which backend you use: - stdout/stderr: logging will 'stutter', with the logs output inside the `Display` implementation cutting other log lines down the center - file: thread will deadlock, and all future logs will also deadlock There are two mitigations you can make, both completely fix this error. The simplest mitigation to this is to enable the `meta-logging-in-format` feature of `fern`. The disadvantage is that this means fern makes an additional allocation per log call per affected backend. Not a huge cost, but enough to mean it's disabled by default. To enable this, use the following in your `Cargo.toml`: ```toml [dependencies] # ... fern = { version = "0.5", features = ["meta-logging-in-format"] } ``` The second mitigation is one you can make inside a formatting closure. This means extra code complexity, but it also means you can enable it per-logger: the fix isn't global. This fix is also redundant if you've already enable the above feature. To add the second mitigation, replacec `format_args!()` with `format!()` as displayed below: ``` fern::Dispatch::new() # /* ... # */ // instead of doing this: .format(move |out, message, record| { out.finish(format_args!("[{}] {}", record.level(), message)) }) // do this: .format(move |out, message, record| { let formatted = format!("[{}] {}", record.level(), message); out.finish(format_args!("{}", formatted)) }) # ; ``` This second mitigation works by forcing the `Display` implementation to run before any text has started to log to the backend. There's an additional allocation per log, but it no longer deadlocks! This mitigation also has the advantage of ensuring there's only one call to `Display::fmt`. If youc use `meta-logging-in-format` and have multiple backends, `Display::fmt` will still be called once per backend. With this, it will only be called once. ------ If you've never experienced this problem, there's no need to fix it - `Display::fmt` and `Debug::fmt` are normally implemented as "pure" functions with no side effects. */ fern-0.5.8/src/syslog.rs010064400017500000144000000103431331227371300133530ustar0000000000000000/*! Example usage of `fern` with the `syslog` crate. Be sure to depend on `syslog` and the `syslog` feature in `Cargo.toml`: ```toml [dependencies] fern = { version = "0.5", features = ["syslog-4"] }] syslog = "4" ``` To use `syslog`, simply create the log you want, and pass it into `Dispatch::chain`: ```no_run extern crate fern; extern crate syslog; # fn setup_logging() -> Result<(), Box> { let formatter = syslog::Formatter3164 { facility: syslog::Facility::LOG_USER, hostname: None, process: "hello-world".to_owned(), pid: 0, }; fern::Dispatch::new() .chain(syslog::unix(formatter)?) .apply()?; # Ok(()) # } # fn main() { setup_logging().ok(); } ``` --- ## Alternate syslog versions If you're using syslog=4.0.0 exactly, one line "ok" will be printed to stdout on log configuration. This is [a bug in syslog](https://github.com/Geal/rust-syslog/issues/39), and there is nothing we can change in fern to fix that. One way to avoid this is to use an earlier version of syslog, which `fern` also supports. To do this, depend on `syslog = 3` instead. ```toml [dependencies] fern = { version = "0.5", features = ["syslog-3"] }] syslog = "3" ``` The setup is very similar, except with less configuration to start the syslog logger: ```rust extern crate fern; # /* extern crate syslog; # */ extern crate syslog3 as syslog; # fn setup_logging() -> Result<(), Box> { fern::Dispatch::new() .chain(syslog::unix(syslog::Facility::LOG_USER)?) .apply()?; # Ok(()) # } # fn main() { setup_logging().ok(); } ``` The rest of this document applies to both syslog 3 and syslog 4, but the examples will be using syslog 4 as it is the latest version. --- One thing with `syslog` is that you don't generally want to apply any log formatting. The system logger will handle that for you. However, you probably will want to format messages you also send to stdout! Fortunately, selective configuration is easy with fern: ```no_run # extern crate fern; # extern crate log; # extern crate syslog; # # fn setup_logging() -> Result<(), Box> { let syslog_formatter = syslog::Formatter3164 { facility: syslog::Facility::LOG_USER, hostname: None, process: "hello-world".to_owned(), pid: 0, }; // top level config fern::Dispatch::new() .chain( // console config fern::Dispatch::new() .level(log::LevelFilter::Debug) .format(move |out, message, record| { out.finish(format_args!( "[{}] {}", record.level(), message, )) }) .chain(std::io::stdout()) ) .chain( // syslog config fern::Dispatch::new() .level(log::LevelFilter::Info) .chain(syslog::unix(syslog_formatter)?) ) .apply()?; # Ok(()) # } # fn main() { setup_logging().ok(); } ``` With this, all info and above messages will be sent to the syslog with no formatting, and the messages sent to the console will still look nice as usual. --- One last pattern you might want to know: creating a log target which must be explicitly mentioned in order to work. ```no_run # extern crate fern; # extern crate log; # extern crate syslog; # # fn setup_logging() -> Result<(), Box> { # let formatter = syslog::Formatter3164 { # facility: syslog::Facility::LOG_USER, # hostname: None, # process: "hello-world".to_owned(), # pid: 0, # }; fern::Dispatch::new() // by default only accept warning messages from libraries so we don't spam .level(log::LevelFilter::Warn) // but accept Info and Debug if we explicitly mention syslog .level_for("explicit-syslog", log::LevelFilter::Debug) .chain(syslog::unix(formatter)?) .apply()?; # Ok(()) # } # fn main() { setup_logging().ok(); } ``` With this configuration, only warning messages will get through by default. If we do want to send info or debug messages, we can do so explicitly: ```no_run # #[macro_use] # extern crate log; # fn main() { debug!("this won't get through"); // especially useful if this is from library you depend on. info!("neither will this"); warn!("this will!"); info!(target: "explicit-syslog", "this will also show up!"); # } ``` */ fern-0.5.8/tests/channel_logging.rs010064400017500000144000000010611322037666200155260ustar0000000000000000//! Tests! extern crate fern; extern crate log; extern crate tempdir; use log::Level::*; mod support; use support::manual_log; #[test] fn test_channel_logging() { use std::sync::mpsc; // Create the channel let (send, recv) = mpsc::channel(); let (_max_level, logger) = fern::Dispatch::new().chain(send).into_log(); let l = &*logger; manual_log(l, Info, "message1"); manual_log(l, Info, "message2"); logger.flush(); assert_eq!(recv.recv().unwrap(), "message1\n"); assert_eq!(recv.recv().unwrap(), "message2\n"); } fern-0.5.8/tests/enabled_is_deep_check.rs010064400017500000144000000011071344633150700166270ustar0000000000000000//! See https://github.com/daboross/fern/issues/38 extern crate fern; #[macro_use] extern crate log; #[test] fn ensure_enabled_is_a_deep_check() { let dummy = fern::Dispatch::new() .level(log::LevelFilter::Warn) .chain(std::io::stdout()); let stdout = fern::Dispatch::new() .level(log::LevelFilter::Info) .level_for("abc", log::LevelFilter::Debug) .chain(std::io::stdout()); fern::Dispatch::new() .chain(stdout) .chain(dummy) .apply() .unwrap(); assert!(!log_enabled!(log::Level::Debug)); } fern-0.5.8/tests/file_logging.rs010064400017500000144000000077311330631716500150450ustar0000000000000000//! Tests! extern crate fern; extern crate log; extern crate tempdir; use std::io::prelude::*; use std::{fs, io}; use log::Level::*; mod support; use support::manual_log; #[test] fn test_basic_logging_file_logging() { // Create a temporary directory to put a log file into for testing let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory"); let log_file = temp_log_dir.path().join("test.log"); { // Create a basic logger configuration let (_max_level, logger) = fern::Dispatch::new() .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg))) .level(log::LevelFilter::Info) .chain(io::stdout()) .chain(fern::log_file(log_file).expect("Failed to open log file")) .into_log(); let l = &*logger; manual_log(l, Trace, "SHOULD NOT DISPLAY"); manual_log(l, Debug, "SHOULD NOT DISPLAY"); manual_log(l, Info, "Test information message"); manual_log(l, Warn, "Test warning message"); manual_log(l, Error, "Test error message"); // ensure all File objects are dropped and OS buffers are flushed. log::logger().flush(); { let result = { let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap(); let mut buf = String::new(); log_read.read_to_string(&mut buf).unwrap(); buf }; assert!( !result.contains("SHOULD NOT DISPLAY"), "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n", result ); assert!( result.contains("[INFO] Test information message"), "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n", result ); assert!( result.contains("[WARN] Test warning message"), "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n", result ); assert!( result.contains("[ERROR] Test error message"), "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n", result ); } } // ensure logger is dropped before temp dir temp_log_dir .close() .expect("Failed to clean up temporary directory"); } #[test] fn test_custom_line_separators() { // Create a temporary directory to put a log file into for testing let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory"); let log_file = temp_log_dir.path().join("test_custom_line_sep.log"); { // Create a basic logger configuration let (_max_level, logger) = fern::Dispatch::new() // default format is just the message if not specified // default log level is 'trace' if not specified (logs all messages) // output to the log file with the "\r\n" line separator. .chain(fern::Output::file(fern::log_file(&log_file).expect("Failed to open log file"), "\r\n")) .into_log(); let l = &*logger; manual_log(l, Info, "message1"); manual_log(l, Info, "message2"); // ensure all File objects are dropped and OS buffers are flushed. logger.flush(); { let result = { let mut log_read = fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap(); let mut buf = String::new(); log_read.read_to_string(&mut buf).unwrap(); buf }; assert_eq!(&result, "message1\r\nmessage2\r\n"); } } // ensure logger is dropped before temp dir temp_log_dir .close() .expect("Failed to clean up temporary directory"); } fern-0.5.8/tests/global_logging.rs010064400017500000144000000054551325564515200153720ustar0000000000000000//! Tests! extern crate fern; #[macro_use] extern crate log; use std::sync::{Arc, Mutex}; /// Custom logger built to verify our exact test case. struct LogVerify { info: bool, warn: bool, error: bool, } impl LogVerify { fn new() -> Self { LogVerify { info: false, warn: false, error: false, } } fn log(&mut self, record: &log::Record) { let formatted_message = format!("{}", record.args()); match &*formatted_message { "[INFO] Test information message" => { assert_eq!(self.info, false, "expected only one info message"); self.info = true; } "[WARN] Test warning message" => { assert_eq!(self.warn, false, "expected only one warn message"); self.warn = true; } "[ERROR] Test error message" => { assert_eq!(self.error, false, "expected only one error message"); self.error = true; } other => panic!("unexpected message: '{}'", other), } } } /// Wrapper for our verification which acts as the actual logger. #[derive(Clone)] struct LogVerifyWrapper(Arc>); impl LogVerifyWrapper { fn new() -> Self { LogVerifyWrapper(Arc::new(Mutex::new(LogVerify::new()))) } fn cloned_boxed_logger(&self) -> Box { Box::new(self.clone()) } } impl log::Log for LogVerifyWrapper { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { self.0.lock().unwrap().log(record); } fn flush(&self) {} } #[test] fn test_global_logger() { let verify = LogVerifyWrapper::new(); // Create a basic logger configuration fern::Dispatch::new() .format(|out, msg, record| { out.finish(format_args!("[{}] {}", record.level(), msg)) }) // Only log messages Info and above .level(log::LevelFilter::Info) // Output to our verification logger for verification .chain(verify.cloned_boxed_logger()) .apply() .expect("Failed to initialize logger: global logger already set!"); trace!("SHOULD NOT DISPLAY"); debug!("SHOULD NOT DISPLAY"); info!("Test information message"); warn!("Test warning message"); error!("Test error message"); // ensure all buffers are flushed. log::logger().flush(); let verify_acquired = verify.0.lock().unwrap(); assert_eq!( verify_acquired.info, true, "expected info message to be received" ); assert_eq!( verify_acquired.warn, true, "expected warn message to be received" ); assert_eq!( verify_acquired.error, true, "expected error message to be received" ); } fern-0.5.8/tests/meta_logging.rs010064400017500000144000000053731330631727500150560ustar0000000000000000//! This provides testing of the 'meta-logging' feature, which allows for //! deadlock-free logging within logging formatters. //! //! These tests *will* deadlock if the feature is not enabled, so they're //! disabled by default. #![cfg(feature = "meta-logging-in-format")] extern crate fern; extern crate log; extern crate tempdir; mod support; use std::io::prelude::*; use std::{fmt, fs, io}; use log::Level::*; use log::Log; use support::manual_log; // in order to actually trigger the situation that deadlocks, we need a custom // Display implementation which performs logging: struct VerboseDisplayThing<'a> { log_copy: &'a Log, msg: &'a str, } impl<'a> fmt::Display for VerboseDisplayThing<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { manual_log( self.log_copy, Debug, format_args!( "VerboseDisplayThing is being displayed! [contents: {}]", self.msg ), ); f.write_str(self.msg) } } #[test] fn file_deadlock() { // Create a temporary directory to put a log file into for testing let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory"); let log_file = temp_log_dir.path().join("test.log"); { let (_max_level, logger) = fern::Dispatch::new() .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg))) .chain(io::stdout()) .chain(fern::log_file(log_file).expect("Failed to open log file")) .into_log(); let l = &*logger; manual_log( l, Info, format_args!( "Hello, world! {}", VerboseDisplayThing { log_copy: l, msg: "it's verbose!", } ), ); // ensure all File objects are dropped and OS buffers are flushed. log::logger().flush(); { let contents = { let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap(); let mut buf = String::new(); log_read.read_to_string(&mut buf).unwrap(); buf }; assert_eq!( contents, // double logs because we're logging to stdout & the file "[DEBUG] VerboseDisplayThing is being displayed! [contents: it's verbose!]\ \n[DEBUG] VerboseDisplayThing is being displayed! [contents: it's verbose!]\ \n[INFO] Hello, world! it's verbose!\n" ); } } // ensure logger is dropped before temp dir temp_log_dir .close() .expect("Failed to clean up temporary directory"); } fern-0.5.8/tests/panic_logging.rs010064400017500000144000000021121325564515200152070ustar0000000000000000//! Test the functionality of panicking on error+ log messages. extern crate fern; extern crate log; use log::Level::*; mod support; use support::manual_log; #[test] #[should_panic(expected = "special panic message here")] fn test_panic_panics() { let (_max_level, logger) = fern::Dispatch::new().chain(fern::Panic).into_log(); let l = &*logger; manual_log(l, Info, "special panic message here"); } fn warn_and_higher_panics_config() -> Box { let (_max_level, logger) = fern::Dispatch::new() .chain( fern::Dispatch::new() .level(log::LevelFilter::Warn) .chain(fern::Panic), ) .chain(std::io::stdout()) .into_log(); logger } #[test] fn double_chained_with_panics_no_info_panic() { let l = &*warn_and_higher_panics_config(); manual_log(l, Info, "this should not panic"); } #[test] #[should_panic(expected = "this should panic")] fn double_chained_with_panics_yes_error_panic() { let l = &*warn_and_higher_panics_config(); manual_log(l, Error, "this should panic"); } fern-0.5.8/tests/support.rs010064400017500000144000000007201331156513500141210ustar0000000000000000//! Support module for tests extern crate log; use std::fmt; /// Utility to manually enter a log message into a logger. All extra metadata /// (target, line number, etc) will be blank. pub fn manual_log(logger: &T, level: log::Level, message: U) where T: log::Log + ?Sized, U: fmt::Display, { logger.log( &log::RecordBuilder::new() .args(format_args!("{}", message)) .level(level) .build(), ); } fern-0.5.8/tests/write_logging.rs010064400017500000144000000031421330631716500152500ustar0000000000000000//! Tests for the raw write logging functionality. extern crate fern; extern crate log; use std::io; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use log::Level::*; mod support; use support::manual_log; #[test] fn test_raw_write_logging() { struct TestWriter { buf: Vec, flag: Arc, } impl io::Write for TestWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf.write(buf) } fn flush(&mut self) -> io::Result<()> { self.buf.flush()?; let expected = b"[INFO] Test information message\n"; if self.buf == expected { self.flag.store(true, Ordering::SeqCst); } else { eprintln!("{:?} does not match {:?}", self.buf, expected); } Ok(()) } } let flag = Arc::new(AtomicBool::new(false)); // Create a basic logger configuration let (_max_level, logger) = fern::Dispatch::new() .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg))) .level(log::LevelFilter::Info) .chain(io::stdout()) .chain(Box::new(TestWriter { buf: Vec::new(), flag: flag.clone(), }) as Box) .into_log(); let l = &*logger; manual_log(l, Info, "Test information message"); // ensure all File objects are dropped and OS buffers are flushed. log::logger().flush(); assert!( flag.load(Ordering::SeqCst), "raw Write test failed: did not match buffer" ); } fern-0.5.8/.cargo_vcs_info.json0000644000000001120000000000000120310ustar00{ "git": { "sha1": "65e7ca901a309bc167249fe7541fdba0baa23836" } }