소스 정보
- 저장소
- hugoduncan/library-skills
- 최근 소스 활동
- 2025년 11월 15일 23:25
- 감지된 SKILL.md 언어
- 영어
- 스타
- 3
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/hugoduncan/library-skills --skill timbre명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Clojure library for spawning sub-processes and shell operations
REPL-friendly data visualization and literate programming for Clojure with Kindly convention
Django-inspired HTML templating system for Clojure with filters, tags, and template inheritance
SOC 직업 분류 기준
SKILL.md 표시 중
| name | timbre |
| description | Pure Clojure/Script logging library with flexible configuration and powerful features |
Pure Clojure/Script logging library with zero dependencies, simple configuration, and powerful features like async logging, rate limiting, and flexible appenders.
Timbre is a logging library designed for Clojure and ClojureScript applications. Unlike Java logging frameworks requiring XML or properties files, Timbre uses pure Clojure data structures for all configuration.
Key characteristics:
Library: com.taoensso/timbre
Latest Version: 6.8.0
License: EPL-1.0
Note: For new projects, consider Telemere - a modern rewrite of Timbre. Existing Timbre users have no pressure to migrate.
;; deps.edn
{:deps {com.taoensso/timbre {:mvn/version "6.8.0"}}}
;; Leiningen
[com.taoensso/timbre "6.8.0"]
Seven standard levels in ascending severity:
:trace - Detailed diagnostic information:debug - Debugging information:info - Informational messages:warn - Warning messages:error - Error messages:fatal - Critical failures:report - Special reporting levelFunctions that handle log output: (fn [data]) -> ?effects
Each appender receives a data map containing:
:level - Log level keyword:?msg - Log message:timestamp - When log occurred:hostname - System hostname:?ns-str - Namespace string:?file, :?line - Source location:?err - Exception (if present)Functions that transform log data: (fn [data]) -> ?data
Applied before appenders receive data, enabling:
Timbre uses a single atom *config* containing:
:min-level - Minimum log level:ns-filter - Namespace filtering:middleware - Middleware functions:timestamp-opts - Timestamp formatting:output-fn - Output formatter:appenders - Appender map(require '[taoensso.timbre :as timbre])
;; Basic logging at different levels
(timbre/trace "Entering function")
(timbre/debug "Variable value:" x)
(timbre/info "Server started on port" port)
(timbre/warn "Deprecated function used")
(timbre/error "Failed to connect to database")
(timbre/fatal "Critical system failure")
(timbre/report "Daily metrics" {:users 1000 :requests 5000})
;; Printf-style formatting
(timbre/infof "User %s logged in from %s" username ip)
(timbre/warnf "Cache miss rate: %.2f%%" miss-rate)
(timbre/errorf "Request failed: %d %s" status-code message)
(try
(risky-operation)
(catch Exception e
(timbre/error e "Operation failed")))
;; Multiple values
(timbre/error e "Failed processing" {:user-id 123 :item-id 456})
;; Log value and return it
(let [result (timbre/spy :info (expensive-calculation x y))]
(process result))
;; With custom message
(timbre/spy :debug
{:msg "Calculation result"}
(expensive-calculation x y))
;; Global minimum level
(timbre/set-min-level! :info)
;; Per-namespace level
(timbre/set-ns-min-level! {:deny #{"noisy.namespace.*"}
:allow #{"important.namespace.*"}})
;; Replace entire config
(timbre/set-config! new-config)
;; Merge into existing config
(timbre/merge-config! {:min-level :debug
:appenders {:println (println-appender)}})
;; Swap with function
(timbre/swap-config! update :min-level (constantly :warn))
;; Temporarily change config
(timbre/with-config custom-config
(timbre/info "Logged with custom config"))
;; Merge temporary changes
(timbre/with-merged-config {:min-level :trace}
(timbre/trace "Temporarily enabled trace logging"))
;; Temporary level change
(timbre/with-min-level :debug
(timbre/debug "Debug enabled for this scope"))
(require '[taoensso.timbre.appenders.core :as appenders])
;; println appender (default)
{:appenders {:println (appenders/println-appender)}}
;; Simple file appender
{:appenders {:spit (appenders/spit-appender
{:fname "/var/log/app.log"})}}
(defn my-appender
"Appender that writes to a custom destination"
[opts]
{:enabled? true
:async? false
:min-level nil ; Inherit from config
:rate-limit nil
:output-fn :inherit
:fn (fn [data]
(let [{:keys [output-fn]} data
formatted (output-fn data)]
;; Custom logic here
(send-to-custom-system formatted)))})
;; Use custom appender
(timbre/merge-config!
{:appenders {:custom (my-appender {})}})
{:enabled? true ; Enable/disable
:async? false ; Async logging?
:min-level :info ; Appender-specific min level
:rate-limit [[5 1000]] ; Max 5 logs per 1000ms
:output-fn :inherit ; Use config's output-fn
:fn (fn [data] ...)} ; Handler function
;; Set context for current thread
(timbre/with-context {:user-id 123 :request-id "abc"}
(timbre/info "Processing request")
(do-work))
;; Add to existing context
(timbre/with-context+ {:session-id "xyz"}
(timbre/info "Additional context"))
;; Add hostname to all logs
(defn add-hostname-middleware
[data]
(assoc data :hostname (get-hostname)))
;; Add timestamp middleware
(defn add-custom-timestamp
[data]
(assoc data :custom-ts (System/currentTimeMillis)))
;; Apply middleware
(timbre/merge-config!
{:middleware [add-hostname-middleware
add-custom-timestamp]})
;; Per-appender rate limit
{:appenders
{:println
{:enabled? true
:rate-limit [[10 1000] ; Max 10 per second
[100 60000]] ; Max 100 per minute
:fn (fn [data] (println (:output-fn data)))}}}
;; At log call site
(timbre/log {:rate-limit [[1 5000]]} ; Max once per 5 seconds
:info "Rate limited message")
;; Enable async for appender
{:appenders
{:async-file
{:enabled? true
:async? true ; Process logs asynchronously
:fn (fn [data]
(spit "/var/log/app.log"
(str (:output-fn data) "\n")
:append true))}}}
;; Log only sometimes (probabilistic)
(timbre/sometimes 0.1 ; 10% probability
(timbre/info "Sampled log message"))
;; Conditional error logging
(timbre/log-errors
(risky-operation)) ; Logs if exception thrown
;; Log and rethrow
(timbre/log-and-rethrow-errors
(risky-operation)) ; Logs then rethrows exception
;; Capture uncaught JVM exceptions (Clojure only)
(timbre/handle-uncaught-jvm-exceptions!)
;; Log errors in futures
(timbre/logged-future
(risky-async-operation))
(defn my-output-fn
[{:keys [level ?ns-str ?msg-fmt vargs timestamp_ ?err]}]
(str
(force timestamp_) " "
(str/upper-case (name level)) " "
"[" ?ns-str "] - "
(apply format ?msg-fmt vargs)
(when ?err (str "\n" (timbre/stacktrace ?err)))))
;; Use custom output
(timbre/merge-config! {:output-fn my-output-fn})
{:timestamp-opts
{:pattern "yyyy-MM-dd HH:mm:ss.SSS"
:locale (java.util.Locale/getDefault)
:timezone (java.util.TimeZone/getDefault)}}
(require '[taoensso.timbre :refer [color-str]])
;; ANSI color output
(defn colored-output-fn
[{:keys [level] :as data}]
(let [base-output (timbre/default-output-fn data)]
(case level
:error (color-str :red base-output)
:warn (color-str :yellow base-output)
:info (color-str :green base-output)
base-output)))
;; Whitelist/blacklist namespaces
(timbre/merge-config!
{:ns-filter
{:deny #{"noisy.lib.*" "chatty.namespace"}
:allow #{"important.module.*"}}})
;; Per-namespace min levels
(timbre/set-ns-min-level!
'{my.app.core :trace
my.app.utils :info
["com.external.*"] :warn})
;; Route tools.logging to Timbre
(require '[taoensso.timbre.tools.logging :as tools-logging])
(tools-logging/use-timbre)
;; Now tools.logging calls use Timbre
(require '[clojure.tools.logging :as log])
(log/info "Routed through Timbre")
Timbre can act as an SLF4J backend for Java logging. Requires additional setup with timbre-slf4j-appender or similar.
(ns myapp.logging
(:require [taoensso.timbre :as timbre]
[taoensso.timbre.appenders.core :as appenders]))
(defn init-logging!
[]
(timbre/merge-config!
{:min-level :info
:ns-filter {:deny #{"verbose.library.*"}}
:middleware []
:timestamp-opts {:pattern "yyyy-MM-dd HH:mm:ss"}
:appenders
{:println (appenders/println-appender)
:spit (appenders/spit-appender
{:fname "/var/log/myapp.log"})}}))
(init-logging!)
;; Log with structured data
(timbre/info "User action"
{:action :login
:user-id 123
:ip "192.168.1.1"
:timestamp (System/currentTimeMillis)})
;; Custom output for structured logs
(defn json-output-fn
[{:keys [level msg_ ?ns-str timestamp_ vargs]}]
(json/write-str
{:timestamp (force timestamp_)
:level level
:namespace ?ns-str
:message (force msg_)
:data (first vargs)}))
(defn config-for-env
[env]
(case env
:dev {:min-level :trace
:appenders {:println (appenders/println-appender)}}
:staging {:min-level :debug
:appenders {:spit (appenders/spit-appender
{:fname "/var/log/staging.log"})}}
:prod {:min-level :info
:appenders {:spit (appenders/spit-appender
{:fname "/var/log/production.log"
:async? true})}}))
(timbre/set-config! (config-for-env :prod))
(defn wrap-logging
[handler]
(fn [request]
(let [start (System/currentTimeMillis)
request-id (str (random-uuid))]
(timbre/with-context {:request-id request-id}
(timbre/info "Request started" {:method (:request-method request)
:uri (:uri request)})
(let [response (handler request)
duration (- (System/currentTimeMillis) start)]
(timbre/info "Request completed"
{:status (:status response)
:duration-ms duration})
response)))))
(defn log-query
[query params]
(timbre/spy :debug
{:msg (str "Executing query: " query)}
(db/execute! query params)))
;; Or with timing
(defn log-slow-queries
[query params]
(let [start (System/currentTimeMillis)
result (db/execute! query params)
duration (- (System/currentTimeMillis) start)]
(when (> duration 1000)
(timbre/warn "Slow query detected"
{:query query
:duration-ms duration}))
result))
;; Basic exception logging
(try
(risky-op)
(catch Exception e
(timbre/error e "Operation failed")))
;; With context
(try
(process-item item)
(catch Exception e
(timbre/error e "Failed to process item"
{:item-id (:id item)
:item-type (:type item)})))
;; Log and continue
(defn safe-process
[items]
(doseq [item items]
(timbre/log-errors
(process-item item))))
;; Log and rethrow
(defn critical-operation
[]
(timbre/log-and-rethrow-errors
(perform-critical-task)))
;; Set via JVM property or env var
;; Only :info and above will be compiled
;; :trace and :debug calls removed at compile time
;; -Dtimbre.min-level=:info
;; Verify elision
(timbre/debug "This won't be in bytecode if min-level >= :info")
;; Offload I/O to background thread
{:appenders
{:file
{:async? true
:fn (fn [data]
;; Expensive I/O operation
(write-to-file data))}}}
;; Arguments evaluated only if level enabled
(timbre/debug (expensive-debug-string)) ; Not called if debug disabled
;; For very expensive operations, use explicit check
(when (timbre/log? :debug)
(timbre/debug (very-expensive-operation)))
(ns myapp.core
(:require [taoensso.timbre :as timbre]))
;; Same API as Clojure
(timbre/info "Running in browser")
;; Configure for browser
(timbre/set-config!
{:level :debug
:appenders
{:console
{:enabled? true
:fn (fn [data]
(let [{:keys [output-fn]} data]
(.log js/console (output-fn data))))}}})
(ns myapp.test
(:require [clojure.test :refer :all]
[taoensso.timbre :as timbre]))
;; Disable logging in tests
(use-fixtures :once
(fn [f]
(timbre/with-merged-config {:min-level :fatal}
(f))))
;; Or capture logs for assertions
(defn with-log-capture
[f]
(let [logs (atom [])]
(timbre/with-merged-config
{:appenders
{:test {:enabled? true
:fn (fn [data]
(swap! logs conj data))}}}
(f logs))))
;; Before (tools.logging)
(require '[clojure.tools.logging :as log])
(log/info "Message")
(log/error e "Error occurred")
;; After (Timbre)
(require '[taoensso.timbre :as timbre])
(timbre/info "Message")
(timbre/error e "Error occurred")
;; Or keep tools.logging imports and use bridge
(require '[taoensso.timbre.tools.logging :as tools-logging])
(tools-logging/use-timbre)
;; Now existing tools.logging code routes to Timbre
Check configuration:
;; Verify config
@timbre/*config*
;; Check min level
(:min-level @timbre/*config*)
;; Verify appenders enabled
(->> @timbre/*config* :appenders vals (filter :enabled?))
;; Check namespace filters
(timbre/may-log? :info) ; In current namespace
(timbre/may-log? :info "some.namespace") ; Specific namespace
;; Enable async for expensive appenders
{:appenders {:file {:async? true ...}}}
;; Add rate limiting
{:appenders {:email {:rate-limit [[1 60000]] ...}}}
;; Use compile-time elision in production
;; -Dtimbre.min-level=:info