Search code examples
loggingfile-iobuffercircular-buffer

Implementing a fixed-size log file, or a circular buffer on disk


I checked this question, but it's not what I'm looking for.

I'm trying to figure out how to cap a log file's size (say, 10MB), and as soon as it's hit, either:

  • start writing to the beginning, rather than appending, or
  • keep appending, but delete the contents from the beginning as I do so

Don't really care about language - as long as it's possible :)


Note: I am aware of the rolling log files approach (hit a target size, rename, and continue logging). I am looking to avoid such a roll.


Solution

  • If you are implementing both the writer and the reader, then you can do something like this:

    struct logentry {
        timestamp  ts;
        char       msg [4000];
    };
    
    class logger {
    private:
        int write_recordnum;  // next record number to write
        int max_recordnum;  // controls maximum size of file
        FILE    *logfile;
    
    public:
        logger (const char *filename, int max_records)
        {
            max_recordnum = max_records;
            logfile = fopen (filename, "a+");
        }
    
        void write_next_entry (const char *msg, ...)
        {
            struct logentry ent;
            // format message into entry
            va_list ap;
            va_start (ap, msg);
            vsnprintf (ent.msg, sizeof(ent.msg), msg, ap);
            va_end (ap);
            ent.ts = gettimestamp();
    
            // position logfile
            if (write_recordnum > max_recordnum)
                write_recordnum = 0;
    
            fseek (logfile, write_recordnum * sizeof (ent), 0);
            fwrite (&ent, 1, sizeof(ent), logfile);
        }
    
        bool read_entry (int recnum, char *msg)
        {
            struct logentry ent;
            if (recnum >= max_recordnum)
                return false;
            fseek (logfile, recnum * sizeof (ent), 0);
            fread (&ent, 1, sizeof(ent), logfile);
            strcpy (msg, ent.msg);
            return true;
        }
    };
    

    The idea is to manage a circular buffer by explicit fixed-size record numbers. Needed is logic to manage whether record N exists, and to check errors.