2525#include <fcntl.h>
2626#include <assert.h>
2727#include <errno.h>
28+ #include <ctype.h>
2829
2930#include "memcached/util.h"
3031
3839#define CMDLOG_FILENAME_LENGTH CMDLOG_DIRPATH_LENGTH + 128
3940#define CMDLOG_FILENAME_FORMAT "%s/command_%d_%d_%d_%d.log"
4041
42+ #define CMDLOG_FILTER_MAXNUM 10
43+ #define CMDLOG_FILTER_CMD_MAXLEN 15
44+ #define CMDLOG_FILTER_KEY_MAXLEN 16000
45+
4146/* cmdlog state */
4247#define CMDLOG_NOT_STARTED 0 /* not started */
4348#define CMDLOG_OVERFLOW_STOP 1 /* stop by command log overflow */
@@ -59,6 +64,12 @@ struct cmd_log_buffer {
5964 uint32_t last ;
6065};
6166
67+ /* command log filter structure */
68+ struct cmd_log_filter {
69+ char command [CMDLOG_FILTER_CMD_MAXLEN + 1 ];
70+ char key [CMDLOG_FILTER_KEY_MAXLEN + 1 ];
71+ };
72+
6273/*command log flush structure */
6374struct cmd_log_flush {
6475 pthread_t tid ; /* flush thread id */
@@ -85,6 +96,8 @@ struct cmd_log_global {
8596 struct cmd_log_buffer buffer ;
8697 struct cmd_log_flush flush ;
8798 struct cmd_log_stats stats ;
99+ struct cmd_log_filter filters [CMDLOG_FILTER_MAXNUM ];
100+ int nfilters ;
88101 volatile bool reqstop ;
89102};
90103struct cmd_log_global cmdlog ;
@@ -397,25 +410,42 @@ char *cmdlog_stats(void)
397410 return str ;
398411}
399412
400- void cmdlog_write (char * client_ip , char * command )
413+ void cmdlog_write (char * client_ip , char * command , int cmdlen )
401414{
402415 struct tm * ptm ;
403416 struct timeval val ;
404417 struct cmd_log_buffer * buffer = & cmdlog .buffer ;
405418 char inputstr [CMDLOG_INPUT_SIZE ];
419+ char * ptr = command ;
406420 int inputlen ;
407421 int nwritten ;
422+ int seglen ;
408423
409424 gettimeofday (& val , NULL );
410425 ptm = localtime (& val .tv_sec );
411426
412- nwritten = snprintf (inputstr , CMDLOG_INPUT_SIZE , "%02d:%02d:%02d.%06ld %s %s\n" ,
413- ptm -> tm_hour , ptm -> tm_min , ptm -> tm_sec , (long )val .tv_usec , client_ip , command );
427+ nwritten = snprintf (inputstr , CMDLOG_INPUT_SIZE , "%02d:%02d:%02d.%06ld %s " ,
428+ ptm -> tm_hour , ptm -> tm_min , ptm -> tm_sec , (long )val .tv_usec , client_ip );
429+
430+ while (ptr < command + cmdlen && nwritten < CMDLOG_INPUT_SIZE ) {
431+ seglen = snprintf (inputstr + nwritten , CMDLOG_INPUT_SIZE - nwritten , "%s" , ptr );
432+ nwritten += seglen ;
433+ ptr += seglen ;
434+
435+ if (ptr < command + cmdlen && nwritten < CMDLOG_INPUT_SIZE ) {
436+ inputstr [nwritten ++ ] = ' ' ;
437+ ptr ++ ;
438+ }
439+ }
440+
414441 /* truncated ? */
415- if (nwritten > CMDLOG_INPUT_SIZE ) {
442+ if (nwritten >= CMDLOG_INPUT_SIZE ) {
416443 inputstr [CMDLOG_INPUT_SIZE - 4 ] = '.' ;
417444 inputstr [CMDLOG_INPUT_SIZE - 3 ] = '.' ;
418445 inputstr [CMDLOG_INPUT_SIZE - 2 ] = '\n' ;
446+ } else {
447+ inputstr [nwritten ++ ] = '\n' ;
448+ inputstr [nwritten ] = '\0' ;
419449 }
420450 inputlen = strlen (inputstr );
421451
@@ -447,3 +477,130 @@ void cmdlog_write(char *client_ip, char *command)
447477 }
448478 pthread_mutex_unlock (& buffer -> lock );
449479}
480+
481+ static int get_key_idx_by_command (const char * command )
482+ {
483+ assert (command );
484+ if (strstr (command , "mget" ) ||
485+ strcmp (command , "scan" ) == 0 ||
486+ strcmp (command , "flush" ) == 0 ||
487+ strcmp (command , "scrub" ) == 0 ||
488+ strcmp (command , "stats" ) == 0 ||
489+ strcmp (command , "config" ) == 0 ||
490+ strcmp (command , "cmdlog" ) == 0 ||
491+ strcmp (command , "lqdetect" ) == 0 ||
492+ strcmp (command , "dump" ) == 0 ||
493+ strcmp (command , "zkensemble" ) == 0 ||
494+ strcmp (command , "help" ) == 0 ||
495+ strcmp (command , "shutdown" ) == 0 ||
496+ strcmp (command , "reload" ) == 0 ) {
497+ return -1 ;
498+ } else if ((command [0 ] == 'l' ||
499+ command [0 ] == 's' ||
500+ command [0 ] == 'm' ||
501+ command [0 ] == 'b' ) &&
502+ strcmp (command + 1 , "op" ) == 0 ) {
503+ return 2 ;
504+ }
505+
506+ return 1 ;
507+ }
508+
509+ bool is_cmdlog_filter_match (token_t * tokens , size_t ntokens )
510+ {
511+ assert (ntokens >= 2 );
512+ char command [CMDLOG_FILTER_CMD_MAXLEN + 1 ] = "" ;
513+ char key [CMDLOG_FILTER_KEY_MAXLEN + 1 ] = "" ;
514+ int key_idx = -1 ;
515+
516+ if (cmdlog .nfilters == 0 ) {
517+ return true;
518+ }
519+
520+ if (ntokens >= 3 && strcmp (tokens [0 ].value + 1 , "op" ) == 0 ) {
521+ snprintf (command , CMDLOG_FILTER_CMD_MAXLEN + 1 , "%3s_%s" , tokens [0 ].value , tokens [1 ].value );
522+ } else {
523+ snprintf (command , CMDLOG_FILTER_CMD_MAXLEN + 1 , "%s" , tokens [0 ].value );
524+ }
525+
526+ key_idx = get_key_idx_by_command (command );
527+ if (key_idx > 0 ) {
528+ snprintf (key , CMDLOG_FILTER_KEY_MAXLEN + 1 , "%s" , tokens [key_idx ].value );
529+ }
530+
531+ for (int i = 0 ; i < cmdlog .nfilters ; ++ i ) {
532+ if (cmdlog .filters [i ].command [0 ] == '\0' || strcmp (command , cmdlog .filters [i ].command ) == 0 ) {
533+ if (cmdlog .filters [i ].key [0 ] == '\0' || strcmp (key , cmdlog .filters [i ].key ) == 0 ) {
534+ return true;
535+ }
536+ }
537+ }
538+
539+ return false;
540+ }
541+
542+ int cmdlog_filter_add (const char * command , int command_len , const char * key , int key_len )
543+ {
544+ if (cmdlog .nfilters >= CMDLOG_FILTER_MAXNUM ) {
545+ return 0 ;
546+ }
547+
548+ if (command_len > CMDLOG_FILTER_CMD_MAXLEN || key_len > CMDLOG_FILTER_KEY_MAXLEN ) {
549+ return -1 ;
550+ }
551+
552+ if (key_len > 0 ) {
553+ for (int i = 0 ; key [i ] != '\0' && i < CMDLOG_FILTER_KEY_MAXLEN ; ++ i ) {
554+ if (!isgraph (key [i ])) {
555+ return -1 ;
556+ }
557+ }
558+ }
559+
560+ pthread_mutex_lock (& cmdlog .lock );
561+ strcpy (cmdlog .filters [cmdlog .nfilters ].command , command );
562+ strcpy (cmdlog .filters [cmdlog .nfilters ].key , key );
563+ cmdlog .nfilters ++ ;
564+ pthread_mutex_unlock (& cmdlog .lock );
565+ return 1 ;
566+ }
567+
568+ int cmdlog_filter_remove (int idx , bool remove_all )
569+ {
570+ int nremove = 0 ;
571+
572+ if (idx < 0 || idx >= cmdlog .nfilters ) {
573+ return -1 ;
574+ }
575+
576+ pthread_mutex_lock (& cmdlog .lock );
577+ if (remove_all ) {
578+ nremove += cmdlog .nfilters ;
579+ cmdlog .nfilters = 0 ;
580+ } else {
581+ for (int i = idx ; i < cmdlog .nfilters - 1 ; ++ i ) {
582+ cmdlog .filters [i ] = cmdlog .filters [i + 1 ];
583+ }
584+ nremove ++ ;
585+ cmdlog .nfilters -- ;
586+ }
587+ pthread_mutex_unlock (& cmdlog .lock );
588+ return nremove ;
589+ }
590+
591+ char * cmdlog_filter_list (void )
592+ {
593+ char * buf = (char * )malloc ((cmdlog .nfilters + 1 ) * CMDLOG_INPUT_SIZE );
594+ int nwritten = 0 ;
595+
596+ pthread_mutex_lock (& cmdlog .lock );
597+ if (buf ) {
598+ nwritten += snprintf (buf , CMDLOG_INPUT_SIZE , "\t(%d / %d)\n" , cmdlog .nfilters , CMDLOG_FILTER_MAXNUM );
599+ for (int i = 0 ; i < cmdlog .nfilters ; ++ i ) {
600+ nwritten += snprintf (buf + nwritten , CMDLOG_INPUT_SIZE , "\t%d. command = %s, key = %s\n" ,
601+ i , cmdlog .filters [i ].command , cmdlog .filters [i ].key );
602+ }
603+ }
604+ pthread_mutex_unlock (& cmdlog .lock );
605+ return buf ;
606+ }
0 commit comments