Howto limit the size of the log files

Currently, there is no way of limiting the maximum size of log files produced by RTI Connext. This may be an issue if your environment has limited resources, or you are just interested in the last few log messages. This solution shows a method to set a maximum size to RTI Connext log files. 

Creating RTI Connext Log Files

To create a log file with the log messages that RTI Connext provides, you have to configure the LoggingQosPolicy in the ParticipantFactoryQoS. For example, in the following XML snippet we create a log file under the logs directory with the highest possible verbosity using the timestamped format:

<participant_factory_qos>
    <logging>
        <output_file>logs/rti.log</output_file>
        <verbosity>ALL</verbosity>
        <print_format>TIMESTAMPED</print_format>
    </logging>
</participant_factory_qos>

Limiting the size of RTI Connext Log Files

The purpose of this solution is to describe a method to control the size of the RTI Connext log files and keep just the most recent information (i.e., limit the log history depth). To do this, you need to create a custom class extending NDDSConfigLoggerDevice in your application. This custom logger device needs to overwrite the write() method to manage the size of the file.

Let's start with the class definition:

class MyLoggerDevice : public NDDSConfigLoggerDevice {
private:
    fpos_t _lastMessagePosition;
    unsigned long _maxSizeInKb;
    const char* _endingMessage;
    RTIOsapiSemaphore* _mutex;
    FILE* _file;
public:
    MyLoggerDevice(const char *fileName, unsigned long maxSizeInKb);
    ~MyLoggerDevice();
    virtual void write(const NDDS_Config_LogMessage *message);
};

The constructor opens the log file and creates a mutex. Note that we use a mutex to avoid concurrency problems as the write() method can be called by different threads at the same time.

MyLoggerDevice::MyLoggerDevice(const char *fileName, unsigned long maxSizeInKb) {
    _lastMessagePosition = 0;
    _maxSizeInKb = maxSizeInKb;
    _endingMessage = "\n\nEnd of current log\n\n\n";
    _mutex = RTIOsapiSemaphore_new(RTI_OSAPI_SEMAPHORE_KIND_MUTEX, NULL);
    _file = fopen(fileName, "r+");
    if (_file == NULL) { 
        _file = fopen(fileName, "w+");
        if (_file == NULL) {
            throw std::runtime_error("Could not create file");
        } 
    }   
    else {
        fseek(_file,0,SEEK_END);
    } 
}

The destructor closes the file and deletes the mutex.

MyLoggerDevice::~MyLoggerDevice() {
    fclose(_file);
    RTIOSapiSemaphore_delete(_mutex); 
}

The write() method is implemented as follows:

void MyLoggerDevice::write(const NDDS_Config_LogMessage *message){
    /* Using mutex because this method can be called multiple times*/
    RTIOsapiSemaphore_take(_mutex,NULL);
    /* Going back to the position before writing the "ending message" */
    fseek(_file,_lastMessagePosition,SEEK_SET);
    int spaceToEndOfFile = (1024*_maxSizeInKb/sizeof(char))-ftell(_file);
    /* Checking if the following message fits in our sized file */
    if (spaceToEndOfFile <= strlen(message->text))
    { 
        char buffer[100] = "\n \n";
        for (int i = 0; i <= spaceToEndOfFile/100; i++)
            fputs(buffer,_file); /* Cleaning garbage at end of file */
        fseek(_file,0,SEEK_SET); /* Back to the beginning */
    }
    fputs(message->text, _file);
    /* Remember position before writing the "ending message" */
    this->lastMessagePosition = ftell(_file);
    fputs(_endingMessage, _file);
    RTIOsapiSemaphore_give(_mutex);
}

When the log file reaches _maxSizeInKb, the write() method goes back to the beginning of the file and overwrites the oldest messages. This way, we only keep the newest messages that fit in _maxSizeInKb. We use an "ending message" to indicate the actual end of the log file.

Finally, all you have to do is to set MyLoggerDevice as an output device of NDDSConfigLogger in the main() function. Note that in the example, we set a maximum size of 10 kB for the logs/rti.log log file.

MyLoggerDevice* customDevice;
long maxSizeInKb = 10;
try {
    customDevice = new MyLoggerDevice("logs/rti.log",maxSizeInKb);     
} catch (exception &e){
    printf("Error creating MyLoggerDevice\n");
    return -1;
}
NDDSConfigLogger* logger = NDDSConfigLogger::get_instance();
if (!logger->set_output_device(customDevice)) {
    printf("Error setting output device");
} 

You can find the example code attached to this solution.