Tag: logrotate

  • Don’t delete the Kafka GC logs when they are used

    Hi,

    I made a mistake some time ago, and it’s there to hunt me.
    Deleting the normal gc logs including the one it’s already used doesn’t solve anything, it just created a more difficult situation.
    Here is my example:

    /dev/sda1                        50G   42G  5.2G  90% /
    /opt/kafka/logs# ll
    total 34M
    drwxrwxr-x 2 kafka kafka 4.0K Oct 10 19:34 ./
    drwxr-xr-x 7 kafka kafka 4.0K Mar 14  2018 ../
    -rw-rw-r-- 1 kafka kafka    0 Mar 14  2018 controller.log
    -rw-rw-r-- 1 kafka kafka    0 Mar 14  2018 kafka-authorizer.log
    -rw-rw-r-- 1 kafka kafka    0 Mar 14  2018 kafka-request.log
    -rw-rw-r-- 1 kafka kafka 2.9M Oct 11 04:44 log-cleaner.log
    -rw-rw-r-- 1 kafka kafka 6.1M Oct 11 05:24 server.log
    -rw-rw-r-- 1 kafka kafka  25M Oct  4 14:03 state-change.log
    
    lsof +L1 | grep delete
    init        1     root   13w   REG    8,1         106     0     95 /var/log/upstart/systemd-logind.log.1 (deleted)
    init        1     root   14w   REG    8,1        5794     0   2944 /var/log/upstart/kafka-manager.log.1 (deleted)
    java     1630    kafka    3w   REG    8,1 46836567522     0 524939 /opt/kafka-2.11-0.10.1.1/logs/kafkaServer-gc.log (deleted)
    java     1863 dd-agent    4r   REG    8,1     5750256     0 525428 /opt/datadog-agent/bin/agent/dist/jmx/jmxfetch-0.20.1-jar-with-dependencies.jar (deleted)
    java    10749 dd-agent    4r   REG    8,1     5750216     0 525427 /opt/datadog-agent/bin/agent/dist/jmx/jmxfetch-0.20.0-jar-with-dependencies.jar (deleted)
    bash    10928     root    0u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    bash    10928     root    1u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    bash    10928     root    2u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    bash    10928     root  255u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    tail    12378     root    0u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    tail    12378     root    1u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    tail    12378     root    2u   CHR  136,6         0t0     0      9 /dev/pts/6 (deleted)
    tail    12378     root    3r   REG    8,1    52428909     0 525512 /opt/kafka-2.11-0.10.1.1/logs/server.log.1 (deleted)
    java    14692 dd-agent    4r   REG    8,1     5750256     0 526042 /opt/datadog-agent/bin/agent/dist/jmx/jmxfetch-0.20.1-jar-with-dependencies.jar (deleted)
    java    16574 dd-agent    4r   REG    8,1     5750256     0 526041 /opt/datadog-agent/bin/agent/dist/jmx/jmxfetch-0.20.1-jar-with-dependencies.jar (deleted)
    

    Handling gc in versions lower than 1.0.0 is quite tricky. It is best to remove these options from your startup script

    -XX:+DisableExplicitGC -Djava.awt.headless=true -Xloggc:/opt/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

    But taking into consideration that we use a standard puppet module that it’s used by multiple teams it is still to be fixed. Fortunately from 1.0.0, GC is disabled by default.

    In order to fix what i showed you before, process restart is needed and we will do that.

    Cheers

  • Log rotate for Kafka Garbage collect without restart

    Morning,

    If you have a Apache Kafka version which is below 1.0.0 and you don’t have garbage collect rotate as shown here:

    with:

    -Xloggc:/opt/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M

    without:

    -Xloggc:/opt/kafka/bin/../logs/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

    One option is to modify the parameters in order to include them in the process that starts but this involves also restarting the services.

    However, you can use also logrotate daemon with the following configuration, and i will put the block that you need to add on hiera using:

    logrotate::rule:
     'kafka_gc':
       path: '/opt/kafka/logs/kafkaServer-gc.log'
       copytruncate: true
       rotate_every: 'day'
       compress: true
       missingok: true
       su: true
       su_owner: 'kafka'
       su_group: 'kafka'
       ifempty: false
       size: '50M'
       maxsize: '50M'
       rotate: 5
    

    Or if you want to write it in a class, it should look like

    $version = lookup('kafka::version')
            if ($_role =~ /\Akafka/) and ($version != '1.0.0') {
                logrotate::rule { 'kafkagc_logs':
                 path => '/opt/kafka/logs/kafkaServer-gc.log',
                 copytruncate => true,
                 rotate => 5,
                 rotate_every => 'daily',
                 missingok => true,
                 ifempty => false,
                 su => true,
                 su_owner => 'kafka',
                 su_group => 'kafka',
                 size => '50M',
                 maxsize => '50M',
            }                                              
    

    Cheers!

  • Implementing logrotate for kafka

    Hi,

    Yes, we will need to implement also logrotate if we want to keep kafka under control. My solution was with puppet, as you probably expected. After i took a look on the documentation related to log4j properties i this i had a configuration figured out that should look like the following erb template

    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    log4j.rootLogger=INFO, stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.kafkaAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.kafkaAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.kafkaAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.kafkaAppender.File=${kafka.logs.dir}/server.log
    log4j.appender.kafkaAppender.MaxFileSize=<%= @filesize %>
    log4j.appender.kafkaAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.kafkaAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.kafkaAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.stateChangeAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.stateChangeAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.stateChangeAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.stateChangeAppender.File=${kafka.logs.dir}/state-change.log
    log4j.appender.stateChangeAppender.MaxFileSize=<%= @filesize %>
    log4j.appender.stateChangeAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.stateChangeAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.stateChangeAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.requestAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.requestAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.requestAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.requestAppender.File=${kafka.logs.dir}/kafka-request.log
    log4j.appender.requestAppender.MaxFileSize=<%= @filesize%>
    log4j.appender.requestAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.requestAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.requestAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.cleanerAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.cleanerAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.cleanerAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.cleanerAppender.File=${kafka.logs.dir}/log-cleaner.log
    log4j.appender.cleanerAppender.MaxFileSize=<%= @filesize %>
    log4j.appender.cleanerAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.cleanerAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.cleanerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.controlAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.controllerAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.controllerAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.controllerAppender.File=${kafka.logs.dir}/controller.log
    log4j.appender.controllerAppender.MaxFileSize=<%= @filesize %>
    log4j.appender.controllerAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.controllerAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.controllerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    log4j.appender.authorizerAppender=org.apache.log4j.RollingFileAppender
    #log4j.appender.authorizerAppender=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.authorizerAppender.DatePattern='.'yyyy-MM-dd-HH
    log4j.appender.authorizerAppender.File=${kafka.logs.dir}/kafka-authorizer.log
    log4j.appender.authorizerAppender.MaxFileSize=<%= @filesize %>
    log4j.appender.authorizerAppender.MaxBackupIndex=<%= @backupindex %>
    log4j.appender.authorizerAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.authorizerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n
    
    # Turn on all our debugging info
    #log4j.logger.kafka.producer.async.DefaultEventHandler=DEBUG, kafkaAppender
    #log4j.logger.kafka.client.ClientUtils=DEBUG, kafkaAppender
    #log4j.logger.kafka.perf=DEBUG, kafkaAppender
    #log4j.logger.kafka.perf.ProducerPerformance$ProducerThread=DEBUG, kafkaAppender
    #log4j.logger.org.I0Itec.zkclient.ZkClient=DEBUG
    log4j.logger.kafka=INFO, kafkaAppender
    
    log4j.logger.kafka.network.RequestChannel$=WARN, requestAppender
    log4j.additivity.kafka.network.RequestChannel$=false
    
    #log4j.logger.kafka.network.Processor=TRACE, requestAppender
    #log4j.logger.kafka.server.KafkaApis=TRACE, requestAppender
    #log4j.additivity.kafka.server.KafkaApis=false
    log4j.logger.kafka.request.logger=WARN, requestAppender
    log4j.additivity.kafka.request.logger=false
    
    log4j.logger.kafka.controller=TRACE, controllerAppender
    log4j.additivity.kafka.controller=false
    
    log4j.logger.kafka.log.LogCleaner=INFO, cleanerAppender
    log4j.additivity.kafka.log.LogCleaner=false
    
    log4j.logger.state.change.logger=TRACE, stateChangeAppender
    log4j.additivity.state.change.logger=false
    
    #Change this to debug to get the actual audit log for authorizer.
    log4j.logger.kafka.authorizer.logger=WARN, authorizerAppender
    log4j.additivity.kafka.authorizer.logger=false
    
    

    It just adds the two most important values required for a working logrotate system MaxFileSize and MaxBackupIndex. The first tells us what is the max size for a file and the second one the amount of files of each type to be kept.
    In order to use it you need to put it in a class that in my view is constructed as follows:

    class profiles::kafkalogrotate {
      
        $filesize = hiera('kafkalogrotate::size','50MB')
        $backupindex = hiera('kafkalogrotate::backupindex',10)
        validate_string($filesize)
        validate_integer($backupindex)
    	file {"adding_log4j":
    	path => "/opt/kafka/config/log4j.properties",
    	content => template("${module_name}/log4j.properties.erb"),
    	replace => true,
    	owner => 'kafka',
      group => 'kafka',
      mode => '0644',
    	}
    }
    

    I put the path as we generally install kafka in /opt/kafka but that can be changed. In our case restart of kafka brokers is not needed immediately so i just called the class in my kafka manifests.

    That is all, if i think of something additional to what i wrote, i will post it.

    Cheers!