Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: HowTo call logger via macro in modules #45

Closed
iamsebastian opened this issue Sep 14, 2016 · 4 comments
Closed

Question: HowTo call logger via macro in modules #45

iamsebastian opened this issue Sep 14, 2016 · 4 comments

Comments

@iamsebastian
Copy link

Hi. I'm kindly fresh to rust and I'm actually building a nickel-rs powered API.

I would like to implement slog-rs in the API, so I can use the logger within different modules and middlewares, to log data like: users accessing the API.

While I have a main.rs as starting point, a lib.rs as library and dozens of modules like middlewares, controllers, etc., I can't get the point, where I have to put the code to instantiate the logger, to let me call macros from these different modules, middlewares, controllers, etc.

I would be very glad, if you can point me, where I could implement this, so I can call the logging instance from different modules via a macro.

Hope you get, what I try to explain.

@dpc
Copy link
Collaborator

dpc commented Sep 14, 2016

You form your Drain somewhere at the start of your app, depending on your app requirements (do you want to log to text file, console, both? In which formats, at which level). Only the binary (app) author can know the exact requirements how to handle logging, so that's why it happens there first.

From that drain you build your root logger with Logger::root.

Then you pass Logger objects around. You can clone them, you can also build more information in child-Loggers by using Logger::new. Anything that needs to log: libraries, modules, structs itself, should generally accept a Logger during initialization and store it somewhere.

In Iron most probably you want to create one Loggerfor the instance of your server (with information like build version, host, port it is listening on). Then at the beginning of your middleware chain, create a child Logger with per-request infor like: peer address, peer port, url requested etc. Put that child Logger into iron::request::Request::extensions or somewhere else that is being preserved between parts of middleware. (I'm not sure of the details here, you might want to ask Iron devs what is the best place. extensions look to me like it will do).

And that's pretty much it. If you use some libraries that do not use slog and you'd want them to be logged withing slog you might needs to look into slog_stdlog create documentation and examples/ dir of slog ("oldlogging" is the keyword).

In case of troubles please ping me on gitter. I really appreciate feedback and I'm interested in all things good and bad about using slog.

@dpc
Copy link
Collaborator

dpc commented Sep 14, 2016

Oh. You are using nickel, not Iron. With quick look at Nickel I see that there's some support for logging logic in nickel, so it might be worth asking Nickel devs how to do it best (maybe slog-nickel/nickel-slog crate should be created to make it nice&easy). But it looks that:

http://docs.nickel.rs/nickel/struct.Request.html

extensions is the place where you can put your own stuff, so slog::Logger should fit there just fine.

@iamsebastian
Copy link
Author

Thanks so much, for your great feedback.

It's not painless, to understand these concepts of extending existent stuff in rust, when you only became a script-kiddie script-language guy after ten years of work with: js, bash/zsh (some ruby, py, etc.)

I will report back, if I implemented something. Till then, I close this issue, since it's blowing up the issues in this project.

Thanks again.

@iamsebastian
Copy link
Author

iamsebastian commented Apr 4, 2017

Just if someone is searching:

main.rs

fn main() {
    let log = Logger::root(slog_term::streamer().full().build().fuse(),
                           o!("version" => env!("CARGO_PKG_VERSION")));
    let mut srv = Nickel::new();
    srv.utilize(middlewares::LoggingMiddleware::new(log.clone()));
}

middlewares::LoggingMiddleware

use nickel::{Middleware, MiddlewareResult, Request, Response};
use nickel_jwt_session::SessionRequestExtensions;
use slog::Logger;

pub struct LoggingMiddleware {
    log: Logger
}

impl LoggingMiddleware {
    pub fn new(log: Logger) -> LoggingMiddleware {
        LoggingMiddleware {
            log: log,
        }
    }
}

/// Just log some information about restricted access of authorized users.
impl<D> Middleware<D> for LoggingMiddleware {
    fn invoke<'mw, 'conn>(&self, req: &mut Request<'mw, 'conn, D>, mut res: Response<'mw, D>) -> MiddlewareResult<'mw, D> {
        use helpers::request::RequestLogExtension;

        let mut log = self.log.clone();
        let mut path = String::new();

        if let Some(users_mail) = req.authorized_user() {
            log = log.new(o!("user" => users_mail.clone()));
        }

        if let Some(pth) = req.path_without_query() {
            path = pth.to_owned().clone();
        }

        log = log.new(o!("path" => path));

        info!(log, "access");

        // custom Trait, where logger gets set -- see below
        req.set_logger(log);

        res.next_middleware()
    }
}

helpers::request::RequestLogExtension

pub struct Loggr;

impl Key for Loggr { type Value = Logger; }

pub trait RequestLogExtension {
    fn get_logger(&self) -> Logger;
    /// Set the logger drain
    fn set_logger(&mut self, log: Logger);
}

impl<'a, 'b, D> RequestLogExtension for Request<'a, 'b, D> {
    fn get_logger(&self) -> Logger {
        use plugin::Extensible;
        self.extensions().get::<Loggr>().unwrap().clone()
    }

    fn set_logger(&mut self, log: Logger) {
        use plugin::Extensible;
        self.extensions_mut().insert::<Loggr>(log);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants