最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

heroku - Papertrail API returns 401 Unauthorized in Babashka script despite valid token - Stack Overflow

programmeradmin1浏览0评论

I'm using Heroku with the Papertrail add-on to manage my server logs. However, I'm not satisfied with Papertrail’s UI and would like to pull logs locally using their API. To do this, I'm writing a Babashka (Clojure) script.

I have the API token stored in my environment variable PAPERTRAIL_TOKEN (which I verified is correct because Heroku is using the same token and it works). My script uses HTTP Basic Authentication (with the token as the username and an empty password) to query Papertrail's /api/v1/events.json endpoint.

Here is my script

#!/usr/bin/env bb
;; -*- clojure -*-
;; deps: { :deps { cheshire/cheshire {:mvn/version "5.10.1"} } }

(require '[babashka.http-client :as http])
(require '[cheshire.core :as json])

;; Retrieve the API token from the environment.
(def token
  (or (System/getenv "PAPERTRAIL_TOKEN")
      (do
        (println "Error: Please set the PAPERTRAIL_TOKEN environment variable.")
        (System/exit 1))))

;; Read an optional query argument from the command-line.
(def query (first *command-line-args*))

;; Define the Papertrail events API endpoint.
(def url ".json")

;; Build HTTP options with basic authentication (username = token, password = blank).
(def base-opts {:basic-auth [token ""]})
;; If a query was provided, add it as a query parameter "q".
(def opts (if query
            (assoc base-opts :query-params {"q" query})
            base-opts))

;; Make the HTTP GET request.
(let [resp (http/get url opts)]
  (if (= 200 (:status resp))
    (let [data   (json/parse-string (:body resp) true)
          events (:events data)]
      (doseq [e events]
        ;; Print the timestamp and the message for each event.
        (println (:received_at e) "->" (:message e))))
    (do
      (println "Error:" (:status resp) (:body resp))
      (System/exit 1))))

When I run the script, it fails

✗ bb papertrail_logs.bb
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Exceptional status code: 401
Data:     {:status 401, :body "HTTP Basic: Access denied.\n", :version :http1.1, :headers {"cache-control" "no-cache", "content-length" "27", "content-type" "text/html; charset=utf-8", "date" "Thu, 06 Feb 2025 18:46:00 GMT", "status" "401 Unauthorized", "strict-transport-security" "max-age=31536000", "vary" "Origin", "www-authenticate" "Basic realm=\"Papertrail API\""}, :uri #object[java.URI 0x1f2f7832 ".json"], :request {:headers {:accept "*/*", :accept-encoding ["gzip" "deflate"], :user-agent "babashka.http-client/0.4.22", :authorization "Basic Wm16b2JwWnJ1ZU4yUHA5ZHNERjo="}, :basic-auth ["ZmzobpZrueN2Pp9dsDF" ""], :uri #object[java.URI 0x1f2f7832 ".json"], :method :get}}
Location: /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:12

----- Context ------------------------------------------------------------------
25:             (assoc base-opts :query-params {"q" query})
26:             base-opts))
27:
28: ;; Make the HTTP GET request.
29: (let [resp (http/get url opts)]
               ^--- Exceptional status code: 401
30:   (if (= 200 (:status resp))
31:     (let [data   (json/parse-string (:body resp) true)
32:           events (:events data)]
33:       (doseq [e events]
34:         ;; Print the timestamp and the message for each event.

----- Stack trace --------------------------------------------------------------
babashka.http-client.interceptors/fn--28510     - <built-in>
babashka.http-client.internal/then              - <built-in>
babashka.http-client.internal/request/fn--28611 - <built-in>
clojure.core/reduce                             - <built-in>
babashka.http-client.internal/request           - <built-in>
babashka.http-client/request                    - <built-in>
babashka.http-client/get                        - <built-in>
user                                            - /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:12
user                                            - /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:1

My questions are:

Is this the correct way to authenticate with the Papertrail API? According to Papertrail’s API documentation, I understand I should use HTTP Basic Authentication with the API token as the username and an empty password. Am I missing something?

Could there be an issue with how the token is being provided? I've verified the environment variable is set and the token itself is valid. Is it possible that the API requires additional parameters or headers beyond HTTP Basic Auth?

Are there any known quirks with using Babashka’s HTTP client in this scenario? Or might there be an issue with my usage of babashka.http-client?

Any help or insights would be appreciated! Thanks in advance.

I'm using Heroku with the Papertrail add-on to manage my server logs. However, I'm not satisfied with Papertrail’s UI and would like to pull logs locally using their API. To do this, I'm writing a Babashka (Clojure) script.

I have the API token stored in my environment variable PAPERTRAIL_TOKEN (which I verified is correct because Heroku is using the same token and it works). My script uses HTTP Basic Authentication (with the token as the username and an empty password) to query Papertrail's /api/v1/events.json endpoint.

Here is my script

#!/usr/bin/env bb
;; -*- clojure -*-
;; deps: { :deps { cheshire/cheshire {:mvn/version "5.10.1"} } }

(require '[babashka.http-client :as http])
(require '[cheshire.core :as json])

;; Retrieve the API token from the environment.
(def token
  (or (System/getenv "PAPERTRAIL_TOKEN")
      (do
        (println "Error: Please set the PAPERTRAIL_TOKEN environment variable.")
        (System/exit 1))))

;; Read an optional query argument from the command-line.
(def query (first *command-line-args*))

;; Define the Papertrail events API endpoint.
(def url "https://papertrailapp.com/api/v1/events.json")

;; Build HTTP options with basic authentication (username = token, password = blank).
(def base-opts {:basic-auth [token ""]})
;; If a query was provided, add it as a query parameter "q".
(def opts (if query
            (assoc base-opts :query-params {"q" query})
            base-opts))

;; Make the HTTP GET request.
(let [resp (http/get url opts)]
  (if (= 200 (:status resp))
    (let [data   (json/parse-string (:body resp) true)
          events (:events data)]
      (doseq [e events]
        ;; Print the timestamp and the message for each event.
        (println (:received_at e) "->" (:message e))))
    (do
      (println "Error:" (:status resp) (:body resp))
      (System/exit 1))))

When I run the script, it fails

✗ bb papertrail_logs.bb
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Exceptional status code: 401
Data:     {:status 401, :body "HTTP Basic: Access denied.\n", :version :http1.1, :headers {"cache-control" "no-cache", "content-length" "27", "content-type" "text/html; charset=utf-8", "date" "Thu, 06 Feb 2025 18:46:00 GMT", "status" "401 Unauthorized", "strict-transport-security" "max-age=31536000", "vary" "Origin", "www-authenticate" "Basic realm=\"Papertrail API\""}, :uri #object[java.net.URI 0x1f2f7832 "https://papertrailapp.com/api/v1/events.json"], :request {:headers {:accept "*/*", :accept-encoding ["gzip" "deflate"], :user-agent "babashka.http-client/0.4.22", :authorization "Basic Wm16b2JwWnJ1ZU4yUHA5ZHNERjo="}, :basic-auth ["ZmzobpZrueN2Pp9dsDF" ""], :uri #object[java.net.URI 0x1f2f7832 "https://papertrailapp.com/api/v1/events.json"], :method :get}}
Location: /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:12

----- Context ------------------------------------------------------------------
25:             (assoc base-opts :query-params {"q" query})
26:             base-opts))
27:
28: ;; Make the HTTP GET request.
29: (let [resp (http/get url opts)]
               ^--- Exceptional status code: 401
30:   (if (= 200 (:status resp))
31:     (let [data   (json/parse-string (:body resp) true)
32:           events (:events data)]
33:       (doseq [e events]
34:         ;; Print the timestamp and the message for each event.

----- Stack trace --------------------------------------------------------------
babashka.http-client.interceptors/fn--28510     - <built-in>
babashka.http-client.internal/then              - <built-in>
babashka.http-client.internal/request/fn--28611 - <built-in>
clojure.core/reduce                             - <built-in>
babashka.http-client.internal/request           - <built-in>
babashka.http-client/request                    - <built-in>
babashka.http-client/get                        - <built-in>
user                                            - /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:12
user                                            - /Users/pedro/projects/knowledge-base/papertrail_logs.bb:29:1

My questions are:

Is this the correct way to authenticate with the Papertrail API? According to Papertrail’s API documentation, I understand I should use HTTP Basic Authentication with the API token as the username and an empty password. Am I missing something?

Could there be an issue with how the token is being provided? I've verified the environment variable is set and the token itself is valid. Is it possible that the API requires additional parameters or headers beyond HTTP Basic Auth?

Are there any known quirks with using Babashka’s HTTP client in this scenario? Or might there be an issue with my usage of babashka.http-client?

Any help or insights would be appreciated! Thanks in advance.

Share Improve this question asked Feb 6 at 19:09 Pedro DelfinoPedro Delfino 2,7011 gold badge21 silver badges42 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

I solved it with:

#!/usr/bin/env bb
;; -*- clojure -*-
;; deps: { :deps { cheshire/cheshire {:mvn/version "5.10.1"} } }
;; Useful docs: https://www.papertrail.com/help/search-api/


(require '[babashka.http-client :as http])
(require '[cheshire.core :as json])

;; Retrieve the API token from the environment.
(def token
  (or (System/getenv "PAPERTRAIL_TOKEN")
      (do
        (println "Error: Please set the PAPERTRAIL_TOKEN environment variable.")
        (System/exit 1))))

;; Read an optional query argument from the command-line.
(def query (first *command-line-args*))

;; Define the Papertrail events API endpoint.
(def url "https://papertrailapp.com/api/v1/events/search.json")
;; https://papertrailapp.com/api/v1/systems.json.

;; Build HTTP options with basic authentication (username = token, password = blank).
(def base-opts {:headers {"X-Papertrail-Token" token}
                :query-params {"limit" 10000}})
;; If a query was provided, add it as a query parameter "q".
(def opts (if query
            (assoc base-opts :query-params {"q" query})
            base-opts))

;; Make the HTTP GET request.
(let [resp (http/get url opts)]
  (if (= 200 (:status resp))
    (let [data   (json/parse-string (:body resp) true)
          events (:events data)]
      (doseq [e events]
        ;; Print the timestamp and the message for each event.
        (println (:received_at e) "->" (:message e))))
    (do
      (println "Error:" (:status resp) (:body resp))
      (System/exit 1))))

发布评论

评论列表(0)

  1. 暂无评论