diff --git a/OPENAPI_DOC.yml b/OPENAPI_DOC.yml index 9c645b72..3bbc490c 100644 --- a/OPENAPI_DOC.yml +++ b/OPENAPI_DOC.yml @@ -7318,6 +7318,77 @@ paths: application/json: schema: $ref: '#/components/schemas/PlaceOS__Api__Application__ContentError' + /api/engine/v2/modules/{id}/error: + get: + summary: return runtime error log of a module (if any) or 404 + tags: + - Modules + operationId: PlaceOS::Api::Modules_show_error + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/String' + 409: + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__CommonError' + 401: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__CommonError' + 403: + description: Forbidden + 404: + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__CommonError' + 408: + description: Request Timeout + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__CommonError' + 400: + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__ParameterError' + 422: + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__ParameterError' + 406: + description: Not Acceptable + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__ContentError' + 415: + description: Unsupported Media Type + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Api__Application__ContentError' /api/engine/v2/modules/{id}/settings: get: summary: Receive the collated settings for a module @@ -21686,6 +21757,13 @@ components: ignore_startstop: type: boolean nullable: true + has_runtime_error: + type: boolean + nullable: true + error_timestamp: + type: integer + format: Int64 + nullable: true control_system_id: type: string nullable: true @@ -22040,6 +22118,8 @@ components: id: type: string nullable: true + String: + type: string Array_JSON__Any_: type: array items: @@ -22048,8 +22128,6 @@ components: type: object additionalProperties: type: string - String: - type: string PlaceOS__Api__Modules__PingResult: type: object properties: @@ -22550,6 +22628,13 @@ components: ignore_startstop: type: boolean nullable: true + has_runtime_error: + type: boolean + nullable: true + error_timestamp: + type: integer + format: Int64 + nullable: true control_system_id: type: string nullable: true @@ -24941,6 +25026,13 @@ components: ignore_startstop: type: boolean nullable: true + has_runtime_error: + type: boolean + nullable: true + error_timestamp: + type: integer + format: Int64 + nullable: true control_system_id: type: string nullable: true @@ -27391,6 +27483,13 @@ components: ignore_startstop: type: boolean nullable: true + has_runtime_error: + type: boolean + nullable: true + error_timestamp: + type: integer + format: Int64 + nullable: true control_system_id: type: string nullable: true diff --git a/shard.lock b/shard.lock index 5a61416b..ef7f0f70 100644 --- a/shard.lock +++ b/shard.lock @@ -99,7 +99,7 @@ shards: faker: git: https://github.com/askn/faker.git - version: 0.8.0 + version: 0.9.0 future: git: https://github.com/crystal-community/future.cr.git @@ -149,6 +149,10 @@ shards: git: https://github.com/spider-gazelle/log_helper.git version: 1.0.3 + loki-client: + git: https://github.com/spider-gazelle/crystal-loki-client.git + version: 0.1.0+git.commit.ed3c0ab5b6f3da6103f6aed680fdcd171680feca + lucky_router: git: https://github.com/luckyframework/lucky_router.git version: 0.5.2 @@ -231,7 +235,7 @@ shards: placeos-core: git: https://github.com/placeos/core.git - version: 4.14.1+git.commit.f926a1cd888532c1ae816e283568a9c668da04ad + version: 4.14.2+git.commit.f30dd7da934f29a341b23c629abb188fbce30b94 placeos-core-client: # Overridden git: https://github.com/placeos/core-client.git @@ -251,7 +255,7 @@ shards: placeos-models: git: https://github.com/placeos/models.git - version: 9.45.0 + version: 9.47.0 placeos-resource: git: https://github.com/place-labs/resource.git diff --git a/shard.yml b/shard.yml index 3c9d1229..72c2d98e 100644 --- a/shard.yml +++ b/shard.yml @@ -110,6 +110,10 @@ dependencies: stumpy_png: github: stumpycr/stumpy_png + # For Loki Search + loki-client: + github: spider-gazelle/crystal-loki-client + development_dependencies: # Linter ameba: diff --git a/src/placeos-rest-api/controllers/modules.cr b/src/placeos-rest-api/controllers/modules.cr index 6505ae76..026a5f4c 100644 --- a/src/placeos-rest-api/controllers/modules.cr +++ b/src/placeos-rest-api/controllers/modules.cr @@ -1,5 +1,6 @@ require "pinger" require "placeos-driver/storage" +require "loki-client" require "./application" require "./drivers" @@ -19,7 +20,7 @@ module PlaceOS::Api before_action :can_write, only: [:create, :update, :destroy, :remove] before_action :check_admin, except: [:index, :create, :update, :destroy, :state, :show, :ping, :start, :stop] - before_action :check_support, only: [:state, :show, :ping] + before_action :check_support, only: [:state, :show, :ping, :show_error] ############################################################################################### @@ -233,6 +234,25 @@ module PlaceOS::Api end end + # return runtime error log of a module (if any) or 404 + @[AC::Route::GET("/:id/error")] + def show_error : Array(String) + raise Error::NotFound.new("No associated error logs found for module '#{current_module.id}'") unless current_module.has_runtime_error + error_timestamp = current_module.error_timestamp || Time.utc + + client = Loki::Client.from_env + labels = client.list_labels.data + stream = labels.try &.includes?("container") ? "container" : "app" + query = %({#{stream}="core"} | logfmt | source = "#{current_module.id}" | level = "[E]") + results = client.query_range(query, 20, error_timestamp - 1.hour, error_timestamp, Loki::Direction::Backward) + entries = Array(String).new + results.response_data.result.as(Loki::Model::Streams).each do |stream| + stream.entries.each { |entry| entries << (entry.line.try &.gsub("+ ", "") || "\n") } + end + + entries + end + # update the details of a module @[AC::Route::PATCH("/:id", body: :mod)] @[AC::Route::PUT("/:id", body: :mod)]