diff --git a/docs/deployment/logs.md b/docs/deployment/logs.md index 50ba2bb76..887ecf0ba 100644 --- a/docs/deployment/logs.md +++ b/docs/deployment/logs.md @@ -37,7 +37,7 @@ You can use these modifiers as follows: dokku logs node-js-app -t -p web ``` -will show logs continually from the web process. +The above command will show logs continually from the web process. ### Failed deploy logs @@ -127,6 +127,29 @@ dokku logs:vector-stop The `vector` container will be stopped and removed from the system. If the container is not running, this command will do nothing. +#### Checking Vector's Logs + +It may be necessary to check the vector container's logs to ensure that vector is operating as expected. This can be performed with the `logs:vector-logs` command. + +```shell +dokku logs:vector-logs +``` + +This command also supports the following modifiers: + +```shell +--num NUM # the number of lines to display +--tail # continually stream logs +``` + +You can use these modifiers as follows: + +```shell +dokku logs:vector-logs --tail --num 10 +``` + +The above command will show logs continually from the vector container, with an initial history of 10 log lines + #### Configuring a log sink Vector uses the concept of log "sinks" to send logs to a given endpoint. Log sinks may be configured globally or on a per-app basis by specifying a `vector-sink` in DSN form with the `logs:set` command. Specifying a sink value will reload any running vector container. @@ -174,4 +197,13 @@ Valid values for `SINK_TYPE` include all log vector log sinks, while `SINK_OPTIO - `[int]`: form: `key[]=int` - `table`: form: `option[key]=value` +For some sinks - such as the `http` sink - it may be useful to use special characters such as `&`. These characters must be url escaped as per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986.html). + +```shell +# the following command will set the `http` sink with a uri config value +# for a uri config value: https://loggerservice.com:1234/?token=abc1234&type=vector +# the url quoted version: https%3A//loggerservice.com%3A1234/%3Ftoken%3Dabc1234%26type%3Dvector +dokku logs:set test vector-sink "http://?uri=https%3A//loggerservice.com%3A1234/%3Ftoken%3Dabc1234%26type%3Dvector" +``` + Please read the [sink documentation](https://vector.dev/docs/reference/sinks/) for your sink of choice to configure the sink as desired. diff --git a/plugins/common/properties.go b/plugins/common/properties.go index 8b7b58b41..1b687b276 100644 --- a/plugins/common/properties.go +++ b/plugins/common/properties.go @@ -434,7 +434,7 @@ func PropertyWrite(pluginName string, appName string, property string, value str } defer file.Close() - fmt.Fprintf(file, value) + fmt.Fprint(file, value) file.Chmod(0600) SetPermissions(propertyPath, 0600) return nil diff --git a/plugins/logs/functions.go b/plugins/logs/functions.go index 85fcac236..74f2b5146 100644 --- a/plugins/logs/functions.go +++ b/plugins/logs/functions.go @@ -1,6 +1,7 @@ package logs import ( + "bytes" "encoding/json" "errors" "fmt" @@ -128,23 +129,9 @@ func sinkValueToConfig(appName string, sinkValue string) (vectorSink, error) { u.Scheme = strings.ReplaceAll(u.Scheme, "-", "_") - t := fmt.Sprintf("type=%s", u.Scheme) - i := fmt.Sprintf("inputs[]=docker-source:%s", appName) - if appName == "--global" { - i = "inputs[]=docker-global-source" - } - if appName == "--null" { - i = "inputs[]=docker-null-source" - } - - initialQuery := fmt.Sprintf("%s&%s", t, i) query := u.RawQuery - if query == "" { - query = initialQuery - } else if strings.HasPrefix(query, "&") { - query = fmt.Sprintf("%s%s", initialQuery, query) - } else { - query = fmt.Sprintf("%s&%s", initialQuery, query) + if strings.HasPrefix(query, "&") { + query = strings.TrimPrefix(query, "&") } b, err := qson.ToJSON(query) @@ -156,6 +143,15 @@ func sinkValueToConfig(appName string, sinkValue string) (vectorSink, error) { return data, err } + data["type"] = u.Scheme + data["inputs"] = []string{"docker-source:" + appName} + if appName == "--global" { + data["inputs"] = []string{"docker-global-source"} + } + if appName == "--null" { + data["inputs"] = []string{"docker-null-source"} + } + return data, nil } @@ -222,6 +218,8 @@ func writeVectorConfig() error { return err } + b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1) + vectorConfig := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "logs", "vector.json") if err := common.WriteSliceToFile(vectorConfig, []string{string(b)}); err != nil { return err diff --git a/tests/unit/logs.bats b/tests/unit/logs.bats index 9ad5ed65a..e1b640ff7 100644 --- a/tests/unit/logs.bats +++ b/tests/unit/logs.bats @@ -238,6 +238,32 @@ teardown() { assert_output "" } +@test "(logs) logs:set escaped uri" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku logs:set $TEST_APP vector-sink http://?uri=https%3A//loggerservice.com%3A1234/%3Ftoken%3Dabc1234%26type%3Dvector" 2>&1 + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "Setting vector-sink" + assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json" + + run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "http://?uri=https%3A//loggerservice.com%3A1234/%3Ftoken%3Dabc1234%26type%3Dvector" + + run /bin/bash -c "jq -r '.sinks[\"docker-sink:$TEST_APP\"].uri' /var/lib/dokku/data/logs/vector.json" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "https://loggerservice.com:1234/?token=abc1234&type=vector" +} + @test "(logs) logs:set global" { run create_app echo "output: $output"