mirror of https://github.com/coder/coder.git
feat: add level support for startup logs (#7067)
This allows external services like our devcontainer support to display errors and warnings with custom styles to indicate failures to users.
This commit is contained in:
parent
aa2468b16e
commit
81e2b2500a
|
@ -5646,6 +5646,9 @@ const docTemplate = `{
|
|||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -9158,6 +9161,9 @@ const docTemplate = `{
|
|||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -4991,6 +4991,9 @@
|
|||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -8262,6 +8265,9 @@
|
|||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -3706,6 +3706,7 @@ func (q *fakeQuerier) InsertWorkspaceAgentStartupLogs(_ context.Context, arg dat
|
|||
ID: id,
|
||||
AgentID: arg.AgentID,
|
||||
CreatedAt: arg.CreatedAt[index],
|
||||
Level: arg.Level[index],
|
||||
Output: output,
|
||||
})
|
||||
outputLength += int32(len(output))
|
||||
|
|
|
@ -501,7 +501,8 @@ CREATE TABLE workspace_agent_startup_logs (
|
|||
agent_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
output character varying(1024) NOT NULL,
|
||||
id bigint NOT NULL
|
||||
id bigint NOT NULL,
|
||||
level log_level DEFAULT 'info'::log_level NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE workspace_agent_startup_logs_id_seq
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE workspace_agent_startup_logs
|
||||
DROP COLUMN level;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE workspace_agent_startup_logs
|
||||
ADD COLUMN level log_level NOT NULL DEFAULT 'info'::log_level;
|
|
@ -1601,6 +1601,7 @@ type WorkspaceAgentStartupLog struct {
|
|||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Output string `db:"output" json:"output"`
|
||||
ID int64 `db:"id" json:"id"`
|
||||
Level LogLevel `db:"level" json:"level"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentStat struct {
|
||||
|
|
|
@ -111,6 +111,7 @@ func TestInsertWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
AgentID: agent.ID,
|
||||
CreatedAt: []time.Time{database.Now()},
|
||||
Output: []string{"first"},
|
||||
Level: []database.LogLevel{database.LogLevelInfo},
|
||||
// 1 MB is the max
|
||||
OutputLength: 1 << 20,
|
||||
})
|
||||
|
@ -121,6 +122,7 @@ func TestInsertWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
AgentID: agent.ID,
|
||||
CreatedAt: []time.Time{database.Now()},
|
||||
Output: []string{"second"},
|
||||
Level: []database.LogLevel{database.LogLevelInfo},
|
||||
OutputLength: 1,
|
||||
})
|
||||
require.True(t, database.IsStartupLogsLimitError(err))
|
||||
|
|
|
@ -5574,7 +5574,7 @@ func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAge
|
|||
|
||||
const getWorkspaceAgentStartupLogsAfter = `-- name: GetWorkspaceAgentStartupLogsAfter :many
|
||||
SELECT
|
||||
agent_id, created_at, output, id
|
||||
agent_id, created_at, output, id, level
|
||||
FROM
|
||||
workspace_agent_startup_logs
|
||||
WHERE
|
||||
|
@ -5603,6 +5603,7 @@ func (q *sqlQuerier) GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg
|
|||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -5964,21 +5965,23 @@ func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg Inser
|
|||
const insertWorkspaceAgentStartupLogs = `-- name: InsertWorkspaceAgentStartupLogs :many
|
||||
WITH new_length AS (
|
||||
UPDATE workspace_agents SET
|
||||
startup_logs_length = startup_logs_length + $4 WHERE workspace_agents.id = $1
|
||||
startup_logs_length = startup_logs_length + $5 WHERE workspace_agents.id = $1
|
||||
)
|
||||
INSERT INTO
|
||||
workspace_agent_startup_logs
|
||||
workspace_agent_startup_logs (agent_id, created_at, output, level)
|
||||
SELECT
|
||||
$1 :: uuid AS agent_id,
|
||||
unnest($2 :: timestamptz [ ]) AS created_at,
|
||||
unnest($3 :: VARCHAR(1024) [ ]) AS output
|
||||
RETURNING workspace_agent_startup_logs.agent_id, workspace_agent_startup_logs.created_at, workspace_agent_startup_logs.output, workspace_agent_startup_logs.id
|
||||
unnest($3 :: VARCHAR(1024) [ ]) AS output,
|
||||
unnest($4 :: log_level [ ]) AS level
|
||||
RETURNING workspace_agent_startup_logs.agent_id, workspace_agent_startup_logs.created_at, workspace_agent_startup_logs.output, workspace_agent_startup_logs.id, workspace_agent_startup_logs.level
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentStartupLogsParams struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt []time.Time `db:"created_at" json:"created_at"`
|
||||
Output []string `db:"output" json:"output"`
|
||||
Level []LogLevel `db:"level" json:"level"`
|
||||
OutputLength int32 `db:"output_length" json:"output_length"`
|
||||
}
|
||||
|
||||
|
@ -5987,6 +5990,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentStartupLogs(ctx context.Context, arg In
|
|||
arg.AgentID,
|
||||
pq.Array(arg.CreatedAt),
|
||||
pq.Array(arg.Output),
|
||||
pq.Array(arg.Level),
|
||||
arg.OutputLength,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -6001,6 +6005,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentStartupLogs(ctx context.Context, arg In
|
|||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -151,11 +151,12 @@ WITH new_length AS (
|
|||
startup_logs_length = startup_logs_length + @output_length WHERE workspace_agents.id = @agent_id
|
||||
)
|
||||
INSERT INTO
|
||||
workspace_agent_startup_logs
|
||||
workspace_agent_startup_logs (agent_id, created_at, output, level)
|
||||
SELECT
|
||||
@agent_id :: uuid AS agent_id,
|
||||
unnest(@created_at :: timestamptz [ ]) AS created_at,
|
||||
unnest(@output :: VARCHAR(1024) [ ]) AS output
|
||||
unnest(@output :: VARCHAR(1024) [ ]) AS output,
|
||||
unnest(@level :: log_level [ ]) AS level
|
||||
RETURNING workspace_agent_startup_logs.*;
|
||||
|
||||
-- If an agent hasn't connected in the last 7 days, we purge it's logs.
|
||||
|
|
|
@ -256,16 +256,31 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
}
|
||||
createdAt := make([]time.Time, 0)
|
||||
output := make([]string, 0)
|
||||
level := make([]database.LogLevel, 0)
|
||||
outputLength := 0
|
||||
for _, log := range req.Logs {
|
||||
createdAt = append(createdAt, log.CreatedAt)
|
||||
output = append(output, log.Output)
|
||||
outputLength += len(log.Output)
|
||||
if log.Level == "" {
|
||||
// Default to "info" to support older agents that didn't have the level field.
|
||||
log.Level = codersdk.LogLevelInfo
|
||||
}
|
||||
parsedLevel := database.LogLevel(log.Level)
|
||||
if !parsedLevel.Valid() {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid log level provided.",
|
||||
Detail: fmt.Sprintf("invalid log level: %q", log.Level),
|
||||
})
|
||||
return
|
||||
}
|
||||
level = append(level, parsedLevel)
|
||||
}
|
||||
logs, err := api.Database.InsertWorkspaceAgentStartupLogs(ctx, database.InsertWorkspaceAgentStartupLogsParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAt: createdAt,
|
||||
Output: output,
|
||||
Level: level,
|
||||
OutputLength: int32(outputLength),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -1971,5 +1986,6 @@ func convertWorkspaceAgentStartupLog(log database.WorkspaceAgentStartupLog) code
|
|||
ID: log.ID,
|
||||
CreatedAt: log.CreatedAt,
|
||||
Output: log.Output,
|
||||
Level: codersdk.LogLevel(log.Level),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -545,8 +545,9 @@ func (c *Client) PostStartup(ctx context.Context, req PostStartupRequest) error
|
|||
}
|
||||
|
||||
type StartupLog struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Output string `json:"output"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Output string `json:"output"`
|
||||
Level codersdk.LogLevel `json:"level"`
|
||||
}
|
||||
|
||||
type PatchStartupLogs struct {
|
||||
|
|
|
@ -510,4 +510,5 @@ type WorkspaceAgentStartupLog struct {
|
|||
ID int64 `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
Output string `json:"output"`
|
||||
Level LogLevel `json:"level"`
|
||||
}
|
||||
|
|
|
@ -701,6 +701,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta
|
|||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
]
|
||||
|
@ -716,11 +717,22 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta
|
|||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ----------------- | -------- | ------------ | ----------- |
|
||||
| `[array item]` | array | false | | |
|
||||
| `» created_at` | string(date-time) | false | | |
|
||||
| `» id` | integer | false | | |
|
||||
| `» output` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `[array item]` | array | false | | |
|
||||
| `» created_at` | string(date-time) | false | | |
|
||||
| `» id` | integer | false | | |
|
||||
| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | |
|
||||
| `» output` | string | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
| -------- | ------- |
|
||||
| `level` | `trace` |
|
||||
| `level` | `debug` |
|
||||
| `level` | `info` |
|
||||
| `level` | `warn` |
|
||||
| `level` | `error` |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
|
|
@ -217,6 +217,7 @@
|
|||
"logs": [
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
]
|
||||
|
@ -302,16 +303,18 @@
|
|||
```json
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | ------ | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `output` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## agentsdk.Stats
|
||||
|
||||
|
@ -4766,17 +4769,19 @@ Parameter represents a set value for the scope.
|
|||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | ------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `id` | integer | false | | |
|
||||
| `output` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `id` | integer | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentStatus
|
||||
|
||||
|
|
|
@ -1131,6 +1131,7 @@ export interface WorkspaceAgentStartupLog {
|
|||
readonly id: number
|
||||
readonly created_at: string
|
||||
readonly output: string
|
||||
readonly level: LogLevel
|
||||
}
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
|
|
|
@ -73,7 +73,7 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
API.getWorkspaceAgentStartupLogs(ctx.agentID).then((data) =>
|
||||
data.map((log) => ({
|
||||
id: log.id,
|
||||
level: "info" as TypesGen.LogLevel,
|
||||
level: log.level || "info",
|
||||
output: log.output,
|
||||
time: log.created_at,
|
||||
})),
|
||||
|
|
Loading…
Reference in New Issue