Easiest way to print timestamp to ostream
Asked Answered
C

3

8

I'd like to add a timestamp to certain outputs to the std::cout / std::cerr ostreams, without using modified standard streams, like so:

std::cerr << timestamp << "Warning!\n";

or so:

std::cerr << timestamp() << "Warning!\n";

The output should look like this:

[2020-01-23 17:40:15 CET] Warning!

But I'm really not happy with what I've come up with:

class TimeStamp {};

std::ostream &operator<<(std::ostream &stream, const TimeStamp &ts) 
{
    std::time_t t = std::time(nullptr);
    stream << "[" << std::put_time(std::localtime(&t), "%F %T %Z") << "] ";
    return stream;
}

TimeStamp ts;

int main()
{
    std::cerr << ts << "Warning!\n";
    std::cerr << ts << "Another warning!\n";
}

So I'm basically defining an empty class, using a global declaration and overloading the '<<' operator. This feels wrong. A static function like timestamp() is probably better suited, but I'm not quite sure how to go on about this. All the examples I've found online used the overloaded '<<' operator, but it usually made more sense to do so, because some class state was output. Can I locally create an ostream and return that in the function?

Calices answered 23/1, 2020 at 17:11 Comment(2)
using a type that is nothing more than a tag to make operator<< do something special is not wrong at all. Do you see any disadvantages? Why do you think it is wrong?Soredium
I agree that this is a pretty clever solution to your problem. Just mark ts constexprTransportation
P
4

If you're just looking for a standalone function which is what I understood from a "static function like timestamp()" you can just return the date as a string:

std::string timeStamp(){
    std::ostringstream strStream;
    std::time_t t = std::time(nullptr);
    strStream<< "[" << std::put_time(std::localtime(&t), "%F %T %Z") << "] ";
    return strStream.str();
}


int main(){
    std::cout<<timeStamp()<<" Testing!";   
    return 0;
}

Remember to include sstream

Perloff answered 23/1, 2020 at 17:43 Comment(2)
That was quick, thank you! Yeah, I guess std::ostringstream was what I was really looking for in my question. I used std::strftimebefore, but it takes a C-style string and brought some other problems. Thank you again!Calices
Kudos for using ostringstream rather than stringstream direclty. Small things, but by choosing the exact type needed, you are expressing the intent of operation more clearlyWiltonwiltsey
D
5

There's nothing wrong with the way you've done it. But if you're looking for alternatives, you could create an ostream wrapper:

class Logger {
  private:
    std::ostream &stream;

    void print_time() {
        std::time_t t = std::time(nullptr);
        stream << "[" << std::put_time(std::localtime(&t), "%F %T %Z") << "] ";
    }
  public:
    //Maybe also take options for how to log?
    Logger(std::ostream &stream) : stream(stream) { }

    template <typename T>
    std::ostream &operator<<(const T &thing)  {
        print_time();
        return stream << thing;
    }
};

int main()
{
    Logger log(std::cerr);
    log << "Warning!" << std::endl;
    log << "Another warning!" << std::endl;
}

See it run here: https://ideone.com/YRawuQ

Decommission answered 23/1, 2020 at 17:19 Comment(1)
Very fast, thank you! This is really useful indeed, I didn't know I could create a wrapper so easily. I ended up using a combination of things, I've changed my TimeStamp class to a more useful Message class with labels and such, so using a friend ... operator<< made more sense again. But using a ostream wrapper like you suggested is probably the way to go, it seems really handy.Calices
P
4

If you're just looking for a standalone function which is what I understood from a "static function like timestamp()" you can just return the date as a string:

std::string timeStamp(){
    std::ostringstream strStream;
    std::time_t t = std::time(nullptr);
    strStream<< "[" << std::put_time(std::localtime(&t), "%F %T %Z") << "] ";
    return strStream.str();
}


int main(){
    std::cout<<timeStamp()<<" Testing!";   
    return 0;
}

Remember to include sstream

Perloff answered 23/1, 2020 at 17:43 Comment(2)
That was quick, thank you! Yeah, I guess std::ostringstream was what I was really looking for in my question. I used std::strftimebefore, but it takes a C-style string and brought some other problems. Thank you again!Calices
Kudos for using ostringstream rather than stringstream direclty. Small things, but by choosing the exact type needed, you are expressing the intent of operation more clearlyWiltonwiltsey
K
3

You can use standard std::chrono::time_point class from the date and time library to represent the timestamp. Then, you need to convert that timestamp to std::time_t type and, eventually, convert the date and time information from a given calendar time to a character string according to the format string.

auto const now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::cout << std::put_time(std::localtime(&now_time), "%F %T") << std::endl;

For those who want to know more...

You can use the source_location class that represents certain information about the source code, such as file names, line numbers, and function names. It is being merged into ISO C++ and is available for use.

Full code

#include <ctime>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <string_view>
#include <experimental/source_location>

void error(std::string_view const& message,
           std::ostream& os = std::cout,
           std::experimental::source_location const& location = std::experimental::source_location::current()) {
    auto const now = std::chrono::system_clock::now();
    auto now_time = std::chrono::system_clock::to_time_t(now);
    os << "[" << std::put_time(std::localtime(&now_time), "%F %T") << "] "
       << "[ERROR] "
       << location.file_name() << ":"
       << location.line() << " "
       << message << '\n';
}

void info(std::string_view const& message,
          std::ostream& os = std::cout,
          std::experimental::source_location const& location = std::experimental::source_location::current()) {
    auto const now = std::chrono::system_clock::now();
    auto now_time = std::chrono::system_clock::to_time_t(now);
    os << "[" << std::put_time(std::localtime(&now_time), "%F %T") << "] "
       << "[INFO] "
       << location.file_name() << ":"
       << location.line() << " "
       << message << '\n';
}

int main() {
    error("Some error");
    info("Some info");

    // or

    error("Some error 2", std::cerr);
    info("Some info 2", std::cerr);

    return 0;
}

Check it out live

Knudsen answered 23/1, 2020 at 17:23 Comment(7)
where does the requirement of printing file and line come from?Soredium
i mean it isnt wrong, but your good suggestion to use <chrono> is hidden behind stuff that is not obviously related to the quesitonSoredium
you are right... i just wanted to give OP a bit more than he asked and another point of view... I will try to highlight the stuff related to questionKnudsen
I would recommend to take the stream as parameter for those function (perhaps defaulting to std::cerr / std::cout). Hardcoded std::cout can be a pain in the ass sometimesSoredium
@formerlyknownas_463035818 answer updated. tnx for guidanceKnudsen
Thank you for the quick response! I've read about chrono, though I ended up using ctime for easier "datetime" handling, but your code shows me that using chrono really isn't any more work, and more up-to-date, I assume. I didn't know about source_location and even though I didn't specifically ask for it, I'm sure it will come in handy, thank you! The handling via a function that takes a std::string_view argument isn't quite what I had in mind, however I can see that this is another sound approach.Calices
is source_location going to be part of c++20 ?Wiltonwiltsey

© 2022 - 2024 — McMap. All rights reserved.