Bilzard
Bilzard

Reputation: 371

C++ fstream is only saving the first character of a string

As the title says I've a problem with fstream. I've made a singleton logging class which uses fstream to save the logging messages in a file.

I use the IDE XCode. In Visual Studio 2013 the code works fine.

Header file:

#ifndef Logger_hpp
#define Logger_hpp

#include <stdio.h>
#include <fstream>
#include <string>

class Logger
{
private:
    static Logger* loggerPtr;
    std::fstream fs;

    //To ensure no one else can instantiate Logger.
    Logger();

public:
    enum MessageType
    {
        ERROR,
        WARNING,
        INFO
    };

    static Logger* Instance();
    void LogFunc(std::string msg, MessageType type);

};

#endif /* Logger_hpp */

CPP file:

#include "Logger.hpp"
#include <iostream>

Logger* Logger::loggerPtr = NULL;

Logger* Logger::Instance()
{
    if (!loggerPtr)
    {
        loggerPtr = new Logger;
    }

    return loggerPtr;
}

Logger::Logger()
{
    fs.open("docker.log", std::fstream::in | std::fstream::out | std::fstream::app);
}

void Logger::LogFunc(std::string msg, MessageType type)
{
    std::cout << msg;

    switch (type)
    {
        case ERROR:
            fs << msg;
            break;
        case WARNING:
            fs << msg;
            break;
        case INFO:
            fs << msg;
            break;
    }
}

Main file:

int main() {

    Logger::Instance()->LogFunc("Hello.", Logger::INFO);

    std::fstream fs;
    fs.open("docker_test.log", std::fstream::in | std::fstream::out | std::fstream::app);
    fs << "Why does this work?!";

    system("pwd");

    return 0;
}

Log outputs:

cat docker.log -> outputs H
cat docker_test.log -> outputs Why does this work?!

What am I missing? Thanks in advance!

Upvotes: 3

Views: 913

Answers (2)

Galik
Galik

Reputation: 48615

The problem is you never delete your singleton so the std::fstream never gets flushed.

I would do your singleton a little differently. Instead of using a pointer I would use a static instance local to the instance() function like this:

#include <stdio.h>
#include <fstream>
#include <string>
#include <iostream>

class Logger
{
private:
    std::fstream fs;

    //To ensure no one else can instantiate Logger.
    Logger();

public:
    enum MessageType
    {
        ERROR,
        WARNING,
        INFO
    };

    static Logger& Instance(); // return reference
    void LogFunc(std::string msg, MessageType type);

};

Logger& Logger::Instance()
{
    // put the static in the function to ensure creation time
    static Logger logger; // use a static instance, not pointer
    return logger;
}

Logger::Logger()
{
    fs.open("docker.log", std::fstream::in | std::fstream::out | std::fstream::app);
}

void Logger::LogFunc(std::string msg, MessageType type)
{
    std::cout << msg;

    switch (type)
    {
        case ERROR:
            fs << msg;
            break;
        case WARNING:
            fs << msg;
            break;
        case INFO:
            fs << msg;
            break;
    }
}

int main() {

    Logger::Instance().LogFunc("Hello.", Logger::INFO);

    std::fstream fs;
    fs.open("docker_test.log", std::fstream::in | std::fstream::out | std::fstream::app);
    fs << "Why does this work?!";

    system("pwd");

    return 0;
}

Upvotes: 2

Christophe
Christophe

Reputation: 73376

This could be related to the fact that you don't flush your fstream. As it is buffered, the buffer is then not written to the file. As at the end of your code, the Logger is not destroyed (inconvenience of using a raw pointer), the buffer is not flushed either.

It works for the stream you have opened in main(), because the local stream is automaticallt destroyed (and closed) when leaving the function.

Suggestion:

fs << msg;   // for every logging output in syour switch 
fs.flush();  // add a flush

Upvotes: 3

Related Questions