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,151 @@ 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+ int cmdlen = strlen (command );
485+ if (cmdlen < 3 ) {
486+ return -1 ;
487+ }
488+
489+ if ((command [0 ] == 'l' || command [0 ] == 's' || command [0 ] == 'm' || command [0 ] == 'b' ) &&
490+ strcmp (command + 1 , "op" ) == 0 ) {
491+ if ((cmdlen == 8 && memcmp (command , "bop_mget" , 8 ) == 0 ) ||
492+ (cmdlen == 9 && memcmp (command , "bop_smget" , 9 ) == 0 )) {
493+ return -1 ;
494+ }
495+ return 2 ;
496+ } else if ((cmdlen == 3 && memcmp (command , "get" , 3 ) == 0 ) ||
497+ (cmdlen == 4 && memcmp (command , "gets" , 4 ) == 0 ) ||
498+ (cmdlen == 3 && memcmp (command , "gat" , 3 ) == 0 ) ||
499+ (cmdlen == 4 && memcmp (command , "gats" , 4 ) == 0 ) ||
500+ (cmdlen == 3 && memcmp (command , "add" , 3 ) == 0 ) ||
501+ (cmdlen == 3 && memcmp (command , "set" , 3 ) == 0 ) ||
502+ (cmdlen == 7 && memcmp (command , "replace" , 7 ) == 0 ) ||
503+ (cmdlen == 7 && memcmp (command , "prepend" , 7 ) == 0 ) ||
504+ (cmdlen == 6 && memcmp (command , "append" , 6 ) == 0 ) ||
505+ (cmdlen == 3 && memcmp (command , "cas" , 3 ) == 0 ) ||
506+ (cmdlen == 4 && memcmp (command , "incr" , 4 ) == 0 ) ||
507+ (cmdlen == 4 && memcmp (command , "decr" , 4 ) == 0 ) ||
508+ (cmdlen == 6 && memcmp (command , "delete" , 6 ) == 0 ) ||
509+ (cmdlen == 7 && memcmp (command , "getattr" , 7 ) == 0 ) ||
510+ (cmdlen == 7 && memcmp (command , "setattr" , 7 ) == 0 ) ||
511+ (cmdlen == 5 && memcmp (command , "touch" , 5 ) == 0 )) {
512+ return 1 ;
513+ }
514+
515+ return -1 ;
516+ }
517+
518+ bool is_cmdlog_filter_match (token_t * tokens , size_t ntokens )
519+ {
520+ char command [CMDLOG_FILTER_CMD_MAXLEN + 1 ] = "" ;
521+ char * key = "" ;
522+ int key_idx = -1 ;
523+ bool matched = false;
524+
525+ if (cmdlog .nfilters == 0 ) {
526+ return true;
527+ }
528+
529+ if (ntokens < 2 ) {
530+ return false;
531+ }
532+
533+ if (ntokens >= 3 && strcmp (tokens [0 ].value + 1 , "op" ) == 0 ) {
534+ snprintf (command , CMDLOG_FILTER_CMD_MAXLEN + 1 , "%3s_%s" , tokens [0 ].value , tokens [1 ].value );
535+ } else {
536+ snprintf (command , CMDLOG_FILTER_CMD_MAXLEN + 1 , "%s" , tokens [0 ].value );
537+ }
538+
539+ key_idx = get_key_idx_by_command (command );
540+ if ((key_idx == 2 && ntokens >= 4 ) || (key_idx == 1 && ntokens >= 3 )) {
541+ key = tokens [key_idx ].value ;
542+ }
543+
544+ pthread_mutex_lock (& cmdlog .lock );
545+ for (int i = 0 ; i < cmdlog .nfilters ; ++ i ) {
546+ if (cmdlog .filters [i ].command [0 ] == '\0' || strcmp (command , cmdlog .filters [i ].command ) == 0 ) {
547+ if (cmdlog .filters [i ].key [0 ] == '\0' || strcmp (key , cmdlog .filters [i ].key ) == 0 ) {
548+ matched = true;
549+ break ;
550+ }
551+ }
552+ }
553+ pthread_mutex_unlock (& cmdlog .lock );
554+ return matched ;
555+ }
556+
557+ int cmdlog_filter_add (const char * command , int command_len , const char * key , int key_len )
558+ {
559+ int success ;
560+
561+ if (command_len > CMDLOG_FILTER_CMD_MAXLEN || key_len > CMDLOG_FILTER_KEY_MAXLEN ) {
562+ return -1 ;
563+ }
564+
565+ if (key_len > 0 ) {
566+ for (int i = 0 ; key [i ] != '\0' && i < CMDLOG_FILTER_KEY_MAXLEN ; ++ i ) {
567+ if (!isgraph (key [i ])) {
568+ return -1 ;
569+ }
570+ }
571+ }
572+
573+ success = 0 ;
574+ pthread_mutex_lock (& cmdlog .lock );
575+ if (cmdlog .nfilters < CMDLOG_FILTER_MAXNUM ) {
576+ strcpy (cmdlog .filters [cmdlog .nfilters ].command , command );
577+ strcpy (cmdlog .filters [cmdlog .nfilters ].key , key );
578+ cmdlog .nfilters ++ ;
579+ success ++ ;
580+ }
581+ pthread_mutex_unlock (& cmdlog .lock );
582+ return success ;
583+ }
584+
585+ int cmdlog_filter_remove (int idx , bool remove_all )
586+ {
587+ int nremove ;
588+
589+ if (remove_all == false && (idx < 0 || idx >= cmdlog .nfilters )) {
590+ return -1 ;
591+ }
592+
593+ nremove = 0 ;
594+ pthread_mutex_lock (& cmdlog .lock );
595+ if (remove_all ) {
596+ nremove += cmdlog .nfilters ;
597+ cmdlog .nfilters = 0 ;
598+ } else {
599+ for (int i = idx ; i < cmdlog .nfilters - 1 ; ++ i ) {
600+ cmdlog .filters [i ] = cmdlog .filters [i + 1 ];
601+ }
602+ cmdlog .nfilters -- ;
603+ nremove ++ ;
604+ }
605+ pthread_mutex_unlock (& cmdlog .lock );
606+ return nremove ;
607+ }
608+
609+ char * cmdlog_filter_list (void )
610+ {
611+ char * buf = (char * )malloc ((cmdlog .nfilters + 1 ) * CMDLOG_INPUT_SIZE );
612+ int nwritten ;
613+
614+ if (!buf ) {
615+ return NULL ;
616+ }
617+
618+ nwritten = 0 ;
619+ pthread_mutex_lock (& cmdlog .lock );
620+ nwritten = snprintf (buf , CMDLOG_INPUT_SIZE , "\t(%d / %d)\n" , cmdlog .nfilters , CMDLOG_FILTER_MAXNUM );
621+ for (int i = 0 ; i < cmdlog .nfilters ; ++ i ) {
622+ nwritten += snprintf (buf + nwritten , CMDLOG_INPUT_SIZE , "\t%d. command = %s, key = %s\n" ,
623+ i , cmdlog .filters [i ].command , cmdlog .filters [i ].key );
624+ }
625+ pthread_mutex_unlock (& cmdlog .lock );
626+ return buf ;
627+ }
0 commit comments