diff --git a/README.md b/README.md index d534697..f39e5ec 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,12 @@ store. (sl-store-grep-level logs 'warning)) ``` +Topic filters can select multiple topics or invert a match: + +```racket +(sl-store-grep-topic logs '(webview webview-backend)) +(sl-store-grep-topic logs 'webview-backend #:invert? #t) +``` + The Scribble manual documents logger definitions, destinations, log levels, synchronization, and all in-memory store operations. diff --git a/info.rkt b/info.rkt index a536bc8..f2d5f5e 100644 --- a/info.rkt +++ b/info.rkt @@ -1,7 +1,7 @@ #lang info (define pkg-authors '(hnmdijkema)) -(define version "0.2.1") +(define version "0.2.2") (define license 'MIT) (define collection "simple-log") (define pkg-desc "simple-log - A simple wrapper around the racket logging system") diff --git a/private/store.rkt b/private/store.rkt index cb43d8a..767c4a2 100644 --- a/private/store.rkt +++ b/private/store.rkt @@ -43,6 +43,29 @@ (define (level? l) (memq l levels)) +(define (invert-predicate pred invert?) + (if invert? + (lambda (item) (not (pred item))) + pred)) + +(define (topic-filter? value) + (or (symbol? value) + (regexp? value) + (and (list? value) + (andmap (lambda (item) + (or (symbol? item) (regexp? item))) + value)))) + +(define (topic-matches? topic filter) + (cond + ((symbol? filter) + (eq? filter topic)) + ((regexp? filter) + (regexp-match? filter (symbol->string topic))) + ((list? filter) + (ormap (lambda (item) (topic-matches? topic item)) filter)) + (else #f))) + (define (copy-queue q pred) (let ((nq (make-queue))) (sequence-for-each @@ -121,50 +144,53 @@ nq)))) -(define/contract (sl-store-grep-topic store regexp) - (-> sl-store? (or/c symbol? regexp?) sl-store?) - (let ((nq (copy-queue (sl-store-queue store) - (λ (item) - (let ((topic (sl-store-item-topic item))) - (if (symbol? regexp) - (eq? regexp topic) - (regexp-match? regexp (symbol->string topic)))))))) +(define/contract (sl-store-grep-topic store filter #:invert? [invert? #f]) + (->* (sl-store? topic-filter?) (#:invert? boolean?) sl-store?) + (let ((nq (copy-queue + (sl-store-queue store) + (invert-predicate + (lambda (item) + (topic-matches? (sl-store-item-topic item) filter)) + invert?)))) (make-sl-store* (sl-store-max-length store) (queue-length nq) nq))) -(define/contract (sl-store-grep-message store regexp) +(define/contract (sl-store-grep-message store regexp #:invert? [invert? #f]) (-> sl-store? regexp? sl-store?) (let ((nq (copy-queue (sl-store-queue store) - (λ (item) - (let ((msg (sl-store-item-message item))) - (regexp-match? regexp msg)))))) + (invert-predicate + (λ (item) + (let ((msg (sl-store-item-message item))) + (regexp-match? regexp msg))) + invert?)))) (make-sl-store* (sl-store-max-length store) (queue-length nq) nq))) -(define/contract (sl-store-grep store regexp) - (-> sl-store? (or/c symbol? regexp?) sl-store?) +(define/contract (sl-store-grep store value #:invert? [invert? #f]) + (->* (sl-store? (or/c symbol? regexp?)) (#:invert? boolean?) sl-store?) (let ((lvl (hash-ref log-hash - (if (regexp? regexp) - (level-regexp->level regexp #f) - (level-regexp->level (pregexp (symbol->string regexp)) #f)) + (if (regexp? value) + (level-regexp->level value #f) + (level-regexp->level (pregexp (symbol->string value)) #f)) 0))) + (define (matches? item) + (let ((topic (sl-store-item-topic item)) + (level (hash-ref log-hash (sl-store-item-level item) 0)) + (msg (sl-store-item-message item))) + (and (>= level lvl) + (or (if (symbol? value) + (if (level? value) + (or (>= level lvl) (eq? value topic)) + (eq? value topic)) + (regexp-match? value (symbol->string topic))) + (if (symbol? value) + #f + (regexp-match? value msg)))))) (let ((nq (copy-queue (sl-store-queue store) - (λ (item) - (let ((topic (sl-store-item-topic item)) - (level (hash-ref log-hash (sl-store-item-level item) 0)) - (msg (sl-store-item-message item))) - (and (>= level lvl) - (or (if (symbol? regexp) - (if (level? regexp) - (or (>= level lvl) (eq? regexp topic)) - (eq? regexp topic)) - (regexp-match? regexp (symbol->string topic))) - (if (symbol? regexp) - #f - (regexp-match? regexp msg))))))))) + (invert-predicate matches? invert?)))) (make-sl-store* (sl-store-max-length store) (queue-length nq) nq)))) diff --git a/scrbl/simple-log.scrbl b/scrbl/simple-log.scrbl index 1320ecd..b1f6b88 100644 --- a/scrbl/simple-log.scrbl +++ b/scrbl/simple-log.scrbl @@ -220,17 +220,29 @@ Writes all entries to the current output port in the same colon-separated format used by the display and file destinations. } -@defproc[(sl-store-grep-message [store any/c] [regexp regexp?]) any/c]{ +@defproc[(sl-store-grep-message [store any/c] [regexp regexp?] [#:invert? invert? boolean? #f]) any/c]{ Returns a store containing entries whose message matches @racket[regexp]. +When @racket[invert?] is true, entries matching the filter are excluded instead. } @defproc[(sl-store-grep-topic [store any/c] - [topic-or-regexp (or/c symbol? regexp?)]) + [topic-filter + (or/c symbol? + regexp? + (listof (or/c symbol? regexp?)))] + [#:invert? invert? boolean? #f]) any/c]{ With a symbol, returns entries whose topic is exactly that symbol. With a -regular expression, matches against the string form of the topic. +regular expression, matches against the string form of the topic. A list +selects entries matching any listed symbol or regular expression. When +@racket[invert?] is true, entries matching the filter are excluded instead. + +@racketblock[ +(sl-store-grep-topic logs '(webview webview-backend)) +(sl-store-grep-topic logs 'webview-backend #:invert? #t) +] } @defproc[(sl-store-grep-level [store any/c] @@ -254,7 +266,8 @@ matching level. } @defproc[(sl-store-grep [store any/c] - [value (or/c symbol? regexp?)]) + [value (or/c symbol? regexp?)] + [#:invert? invert? boolean? #f]) any/c]{ Performs a combined search. @@ -263,7 +276,8 @@ When @racket[value] is a log-level symbol, the result contains entries at that level or a more severe level. Any other symbol is treated as an exact topic. A regular expression is matched against both topic and message. If that same regular expression also identifies a log level, entries below that level are -excluded. +excluded. When @racket[invert?] is true, matching entries are excluded, similar +to @tt{grep -v}. } @defproc[(sl-store-tail [store any/c]