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

write error and critical logs to stderr #345

Closed
tmplt opened this issue Jan 5, 2017 · 9 comments
Closed

write error and critical logs to stderr #345

tmplt opened this issue Jan 5, 2017 · 9 comments
Labels

Comments

@tmplt
Copy link

tmplt commented Jan 5, 2017

In a project I work on I use spdlog to report errors to the user. To match the behavior of other cli-programs, I want that output written to stderr. Could level::{err,critical} logging be changed to write to stderr, or could a define perhaps be exposed in tweakme.h?

Cheers.

@gabime
Copy link
Owner

gabime commented Jan 5, 2017

You can add an additional stderr sink with its level to level::error.
This way your logger would have 2 sinks with different levels.

@gabime gabime added the question label Jan 5, 2017
@tmplt
Copy link
Author

tmplt commented Jan 5, 2017

The wiki seems to lack the level-setting part, unless I missed it completely. Could you show how this would be done?

@gabime
Copy link
Owner

gabime commented Jan 5, 2017

Just call set_level(..) on any sink:

sink->set_level(spdlog::level::err);

@tmplt
Copy link
Author

tmplt commented Jan 5, 2017

Ah, thanks.

I've come thus far:

auto setup_logger()
{
    std::vector<spdlog::sink_ptr> sinks;

    auto stdout_sink = spdlog::sinks::stdout_sink_mt::instance();
    stdout_sink->set_level(spdlog::level::info);

    auto stderr_sink = spdlog::sinks::stderr_sink_mt::instance();
    auto color_stderr_sink = std::make_shared<spdlog::sinks::ansicolor_sink>(stderr_sink);
    color_stderr_sink->set_level(spdlog::level::err);

    sinks.push_back(stdout_sink);
    sinks.push_back(color_stderr_sink);

    auto logger = std::make_shared<spdlog::logger>("main", std::begin(sinks), std::end(sinks));
    logger->set_pattern("%l: %v");

    return logger;
}

As wanted, error logs are now printed to stderr, but they are also printed to stdout. Can I assign stdout_sink to print everything except for error and critical logs, which color_stderr_sink handles?

On a side note: if I want stdout_sink to be quiet as a default (level::off, I presume), would logger->set_level(spdlog::level::info) affect both sinks?

@tmplt
Copy link
Author

tmplt commented Jan 7, 2017

To answer my own questions:

logger->set_level(spdlog::level::info); does affect both sinks.

And I gather from details/logger_impl.h that it is not possible to assign a singular level or range of levels to a sink: stdout_sink prints err-logs since level::err>level::info. I'm quite tired, so I might have missed someting going through the library. But if I got it all right, would something like sink::limit_level() be a good addition?

@gabime
Copy link
Owner

gabime commented Jan 7, 2017

Yes, you got it right, but note that if for example the logger has "info" level then debug messages won't get forwarded by the logger to the underlying sinks, regardless of their own level.

I am not sure that limit_level() would worth the extra cost just for this rare use case though.. Maybe you could implement a custon sink that wraps those 2 sinks by forwarding the messages according to the level. Should be easy I think.. here is an example of such sink.

@tmplt
Copy link
Author

tmplt commented Jan 7, 2017 via email

@tmplt
Copy link
Author

tmplt commented Jan 7, 2017

The log message level is available in spdlog::details::log_msg, so:

class split_sink : public spdlog::sinks::sink {
    void log(const details::log_msg &msg) override
    {
        if (msg.level <= spdlog::level::warn)
            std::cout << msg.formatted.str();
        else
            std::cerr << msg.formatted.str();
    }

    void flush()
    {
        std::cout << std::flush;
    }
};

is what I came up with.
I now set the logger up with:

auto setup_logger()
{
    auto sink = std::make_shared<split_sink>();
    auto logger = std::make_shared<spdlog::logger>("main", sink);

    logger->set_pattern("%l: %v");
    logger->set_level(spdlog::level::err); // as default

    return logger;
}

Thanks for your help!

@gabime
Copy link
Owner

gabime commented Jan 8, 2017

Looks good. Dont forget protect with mutex if you need thread safety.

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

No branches or pull requests

2 participants