This commit is contained in:
Amaury Chamayou 2021-09-27 13:20:32 +01:00 коммит произвёл GitHub
Родитель 674b5c1e80
Коммит 41cadaf415
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 140 добавлений и 91 удалений

Просмотреть файл

@ -8,7 +8,8 @@ trigger:
jobs: jobs:
- job: build_and_publish_docs - job: build_and_publish_docs
container: ccfciteam/ccf-ci:oe0.17.2-docker-cli container: ccfciteam/ccf-ci:oe0.17.2-docker-cli
pool: 1es-dv4-focal pool:
vmImage: ubuntu-20.04
steps: steps:
- checkout: self - checkout: self

Просмотреть файл

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added ### Added
- Receipts now include the endorsed certificate of the node, as well as its node id, for convenience. - Receipts now include the endorsed certificate of the node, as well as its node id, for convenience.
- `get_metrics_v1` API to `BaseEndpointRegistry` for applications that do not make use of builtins and want to version or customise metrics output.
## [2.0.0-dev4] ## [2.0.0-dev4]

Просмотреть файл

@ -29,6 +29,14 @@ Application Endpoint Registration
:project: CCF :project: CCF
:members: :members:
.. doxygenstruct:: ccf::EndpointMetricsEntry
:project: CCF
:members:
.. doxygenstruct:: ccf::EndpointMetrics
:project: CCF
:members:
.. doxygenclass:: ccf::BaseEndpointRegistry .. doxygenclass:: ccf::BaseEndpointRegistry
:project: CCF :project: CCF
:members: :members:

Просмотреть файл

@ -7,7 +7,18 @@
], ],
"type": "string" "type": "string"
}, },
"EndpointMetrics__Entry": { "EndpointMetrics": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EndpointMetricsEntry": {
"properties": { "properties": {
"calls": { "calls": {
"$ref": "#/components/schemas/uint64" "$ref": "#/components/schemas/uint64"
@ -38,23 +49,12 @@
], ],
"type": "object" "type": "object"
}, },
"EndpointMetrics__Entry_array": { "EndpointMetricsEntry_array": {
"items": { "items": {
"$ref": "#/components/schemas/EndpointMetrics__Entry" "$ref": "#/components/schemas/EndpointMetricsEntry"
}, },
"type": "array" "type": "array"
}, },
"EndpointMetrics__Out": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EntityId": { "EntityId": {
"format": "hex", "format": "hex",
"pattern": "^[a-f0-9]{64}$", "pattern": "^[a-f0-9]{64}$",
@ -338,7 +338,7 @@
"info": { "info": {
"description": "This CCF sample app implements a simple logging application, securely recording messages at client-specified IDs. It demonstrates most of the features available to CCF apps.", "description": "This CCF sample app implements a simple logging application, securely recording messages at client-specified IDs. It demonstrates most of the features available to CCF apps.",
"title": "CCF Sample Logging App", "title": "CCF Sample Logging App",
"version": "1.1.0" "version": "1.2.0"
}, },
"openapi": "3.0.0", "openapi": "3.0.0",
"paths": { "paths": {
@ -365,7 +365,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/EndpointMetrics__Out" "$ref": "#/components/schemas/EndpointMetrics"
} }
} }
}, },

Просмотреть файл

@ -39,7 +39,18 @@
], ],
"type": "string" "type": "string"
}, },
"EndpointMetrics__Entry": { "EndpointMetrics": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EndpointMetricsEntry": {
"properties": { "properties": {
"calls": { "calls": {
"$ref": "#/components/schemas/uint64" "$ref": "#/components/schemas/uint64"
@ -70,23 +81,12 @@
], ],
"type": "object" "type": "object"
}, },
"EndpointMetrics__Entry_array": { "EndpointMetricsEntry_array": {
"items": { "items": {
"$ref": "#/components/schemas/EndpointMetrics__Entry" "$ref": "#/components/schemas/EndpointMetricsEntry"
}, },
"type": "array" "type": "array"
}, },
"EndpointMetrics__Out": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EntityId": { "EntityId": {
"format": "hex", "format": "hex",
"pattern": "^[a-f0-9]{64}$", "pattern": "^[a-f0-9]{64}$",
@ -400,7 +400,7 @@
"info": { "info": {
"description": "This API is used to submit and query proposals which affect CCF's public governance tables.", "description": "This API is used to submit and query proposals which affect CCF's public governance tables.",
"title": "CCF Governance API", "title": "CCF Governance API",
"version": "2.1.0" "version": "2.2.0"
}, },
"openapi": "3.0.0", "openapi": "3.0.0",
"paths": { "paths": {
@ -472,7 +472,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/EndpointMetrics__Out" "$ref": "#/components/schemas/EndpointMetrics"
} }
} }
}, },

Просмотреть файл

@ -7,7 +7,18 @@
], ],
"type": "string" "type": "string"
}, },
"EndpointMetrics__Entry": { "EndpointMetrics": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EndpointMetricsEntry": {
"properties": { "properties": {
"calls": { "calls": {
"$ref": "#/components/schemas/uint64" "$ref": "#/components/schemas/uint64"
@ -38,23 +49,12 @@
], ],
"type": "object" "type": "object"
}, },
"EndpointMetrics__Entry_array": { "EndpointMetricsEntry_array": {
"items": { "items": {
"$ref": "#/components/schemas/EndpointMetrics__Entry" "$ref": "#/components/schemas/EndpointMetricsEntry"
}, },
"type": "array" "type": "array"
}, },
"EndpointMetrics__Out": {
"properties": {
"metrics": {
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
}
},
"required": [
"metrics"
],
"type": "object"
},
"EntityId": { "EntityId": {
"format": "hex", "format": "hex",
"pattern": "^[a-f0-9]{64}$", "pattern": "^[a-f0-9]{64}$",
@ -494,7 +494,7 @@
"info": { "info": {
"description": "This API provides public, uncredentialed access to service and node state.", "description": "This API provides public, uncredentialed access to service and node state.",
"title": "CCF Public Node API", "title": "CCF Public Node API",
"version": "2.1.0" "version": "2.2.0"
}, },
"openapi": "3.0.0", "openapi": "3.0.0",
"paths": { "paths": {
@ -521,7 +521,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/EndpointMetrics__Out" "$ref": "#/components/schemas/EndpointMetrics"
} }
} }
}, },

Просмотреть файл

@ -156,5 +156,10 @@ namespace ccf
/** Get untrusted time from the host of the currently executing node. /** Get untrusted time from the host of the currently executing node.
*/ */
ApiResult get_untrusted_host_time_v1(::timespec& time); ApiResult get_untrusted_host_time_v1(::timespec& time);
/** Get usage metrics from endpoints under the registry, including
* number of calls, errors, failures and retries.
*/
ApiResult get_metrics_v1(EndpointMetrics& endpoint_metrics);
}; };
} }

Просмотреть файл

@ -10,6 +10,7 @@
#include "ds/openapi.h" #include "ds/openapi.h"
#include "http/http_consts.h" #include "http/http_consts.h"
#include "node/certs.h" #include "node/certs.h"
#include "node/endpoint_metrics.h"
#include "node/rpc/serialization.h" #include "node/rpc/serialization.h"
#include <charconv> #include <charconv>

Просмотреть файл

@ -1191,7 +1191,7 @@ namespace loggingapp
"This CCF sample app implements a simple logging application, securely " "This CCF sample app implements a simple logging application, securely "
"recording messages at client-specified IDs. It demonstrates most of " "recording messages at client-specified IDs. It demonstrates most of "
"the features available to CCF apps."; "the features available to CCF apps.";
logger_handlers.openapi_info.document_version = "1.1.0"; logger_handlers.openapi_info.document_version = "1.2.0";
} }
}; };
} }

Просмотреть файл

@ -280,4 +280,25 @@ namespace ccf
return ApiResult::OK; return ApiResult::OK;
} }
ApiResult BaseEndpointRegistry::get_metrics_v1(
EndpointMetrics& endpoint_metrics)
{
endpoint_metrics.metrics.clear();
std::lock_guard<std::mutex> guard(metrics_lock);
for (const auto& [path, verb_metrics] : metrics)
{
for (const auto& [verb, metric] : verb_metrics)
{
endpoint_metrics.metrics.push_back(
{path,
verb,
metric.calls,
metric.errors,
metric.failures,
metric.retries});
}
}
return ApiResult::OK;
}
} }

Просмотреть файл

@ -197,29 +197,26 @@ namespace ccf
.install(); .install();
auto endpoint_metrics_fn = [this](auto&, nlohmann::json&&) { auto endpoint_metrics_fn = [this](auto&, nlohmann::json&&) {
std::lock_guard<std::mutex> guard(metrics_lock); EndpointMetrics out;
EndpointMetrics::Out out; const auto result = get_metrics_v1(out);
for (const auto& [path, verb_metrics] : metrics) if (result == ccf::ApiResult::OK)
{ {
for (const auto& [verb, metric] : verb_metrics) return make_success(out);
{ }
out.metrics.push_back( else
{path, {
verb, return make_error(
metric.calls, HTTP_STATUS_INTERNAL_SERVER_ERROR,
metric.errors, ccf::errors::InternalError,
metric.failures, fmt::format("Error code: {}", ccf::api_result_to_str(result)));
metric.retries});
}
} }
return make_success(out);
}; };
make_command_endpoint( make_command_endpoint(
"/api/metrics", "/api/metrics",
HTTP_GET, HTTP_GET,
json_command_adapter(endpoint_metrics_fn), json_command_adapter(endpoint_metrics_fn),
no_auth_required) no_auth_required)
.set_auto_schema<void, EndpointMetrics::Out>() .set_auto_schema<void, EndpointMetrics>()
.set_execute_outside_consensus( .set_execute_outside_consensus(
ccf::endpoints::ExecuteOutsideConsensus::Locally) ccf::endpoints::ExecuteOutsideConsensus::Locally)
.install(); .install();

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ds/json.h"
#include <vector>
namespace ccf
{
struct EndpointMetricsEntry
{
/// Endpoint path
std::string path;
/// Endpoint method
std::string method;
/// Number of calls since node start
size_t calls = 0;
/// Number of errors (4xx) since node start
size_t errors = 0;
/// Number of failures (5xx) since node start
size_t failures = 0;
/// Number of transaction retries caused by
/// conflicts since node start
size_t retries = 0;
};
struct EndpointMetrics
{
/// Metrics for all endpoints in the frontend
std::vector<EndpointMetricsEntry> metrics;
};
DECLARE_JSON_TYPE(EndpointMetricsEntry)
DECLARE_JSON_REQUIRED_FIELDS(
EndpointMetricsEntry, path, method, calls, errors, failures, retries)
DECLARE_JSON_TYPE(EndpointMetrics)
DECLARE_JSON_REQUIRED_FIELDS(EndpointMetrics, metrics)
}

Просмотреть файл

@ -91,24 +91,6 @@ namespace ccf
using Out = nlohmann::json; using Out = nlohmann::json;
}; };
struct EndpointMetrics
{
struct Entry
{
std::string path;
std::string method;
size_t calls = 0;
size_t errors = 0;
size_t failures = 0;
size_t retries = 0;
};
struct Out
{
std::vector<Entry> metrics;
};
};
struct VerifyReceipt struct VerifyReceipt
{ {
struct In struct In

Просмотреть файл

@ -482,7 +482,7 @@ namespace ccf
openapi_info.description = openapi_info.description =
"This API is used to submit and query proposals which affect CCF's " "This API is used to submit and query proposals which affect CCF's "
"public governance tables."; "public governance tables.";
openapi_info.document_version = "2.1.0"; openapi_info.document_version = "2.2.0";
} }
static std::optional<MemberId> get_caller_member_id( static std::optional<MemberId> get_caller_member_id(

Просмотреть файл

@ -285,7 +285,7 @@ namespace ccf
openapi_info.description = openapi_info.description =
"This API provides public, uncredentialed access to service and node " "This API provides public, uncredentialed access to service and node "
"state."; "state.";
openapi_info.document_version = "2.1.0"; openapi_info.document_version = "2.2.0";
} }
void init_handlers() override void init_handlers() override

Просмотреть файл

@ -117,12 +117,6 @@ namespace ccf
DECLARE_JSON_TYPE(GetNodes::Out) DECLARE_JSON_TYPE(GetNodes::Out)
DECLARE_JSON_REQUIRED_FIELDS(GetNodes::Out, nodes) DECLARE_JSON_REQUIRED_FIELDS(GetNodes::Out, nodes)
DECLARE_JSON_TYPE(EndpointMetrics::Entry)
DECLARE_JSON_REQUIRED_FIELDS(
EndpointMetrics::Entry, path, method, calls, errors, failures, retries)
DECLARE_JSON_TYPE(EndpointMetrics::Out)
DECLARE_JSON_REQUIRED_FIELDS(EndpointMetrics::Out, metrics)
DECLARE_JSON_TYPE(VerifyReceipt::In) DECLARE_JSON_TYPE(VerifyReceipt::In)
DECLARE_JSON_REQUIRED_FIELDS(VerifyReceipt::In, receipt) DECLARE_JSON_REQUIRED_FIELDS(VerifyReceipt::In, receipt)
DECLARE_JSON_TYPE(VerifyReceipt::Out) DECLARE_JSON_TYPE(VerifyReceipt::Out)