NodeJS & Dapr Javascript SDK 官方使用指南
Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。Dapr 确保开发人员专注于编写业务逻辑,不必分神解决分布式系统难题,从而显著提高了生产力。Dapr 降低了构建微服务架构类现代云原生应用的门槛。
系列
- Dapr 与 NestJs ,实战编写一个 Pub & Sub 装饰器
- NodeJS 基于 Dapr 构建云原生微服务应用,从 0 到 1 快速上手指南
- 本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用
JavaScript
用于在 JavaScript 和 TypeScript 中构建 Dapr 应用程序的客户端库。该客户端抽象了公共 Dapr API,例如服务到服务调用、状态管理、发布/订阅、机密等,并为构建应用程序提供了一个简单、直观的 API。
安装
要开始使用 Javascript SDK,请从 NPM 安装 Dapr JavaScript SDK 包:
npm install --save @dapr/dapr
️ dapr-client 现在已弃用。 请参阅#259 了解更多信息。
结构
Dapr Javascript SDK 包含两个主要组件:
- DaprServer: 管理所有 Dapr sidecar 到应用程序的通信。
- DaprClient: 管理所有应用程序到 Dapr sidecar 的通信。
上述通信可以配置为使用 gRPC 或 HTTP 协议。
Client
介绍
Dapr Client 允许您与 Dapr Sidecar 通信并访问其面向客户端的功能,例如发布事件、调用输出绑定、状态管理、Secret 管理等等。
前提条件
- Dapr CLI 已安装
- 初始化 Dapr 环境
- 最新 LTS 版本的 Node 或更高版本
安装和导入 Dapr 的 JS SDK
- 使用 npm 安装 SDK:
npm i @dapr/dapr --save
- 导入库:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";
const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server
// HTTP Example
const client = new DaprClient(daprHost, daprPort);
// GRPC Example
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);
运行
要运行示例,您可以使用两种不同的协议与 Dapr sidecar 交互:HTTP(默认)或 gRPC。
使用 HTTP(默认)
import { DaprClient } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort);
# Using dapr run
dapr run --app-id example-sdk --app-protocol http -- npm run start
# or, using npm script
npm run start:dapr-http
使用 gRPC
由于 HTTP 是默认设置,因此您必须调整通信协议以使用 gRPC。 您可以通过向客户端或服务器构造函数传递一个额外的参数来做到这一点。
import { DaprClient, CommunicationProtocol } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort, CommunicationProtocol.GRPC);
# Using dapr run
dapr run --app-id example-sdk --app-protocol grpc -- npm run start
# or, using npm script
npm run start:dapr-grpc
代理请求
通过代理请求,我们可以利用 Dapr 通过其 sidecar 架构带来的独特功能,例如服务发现、日志记录等,使我们能够立即“升级”我们的 gRPC 服务。 gRPC 代理的这一特性在community call 41 中得到了展示。
community call 41
创建代理
要执行 gRPC 代理,只需调用 client.proxy.create() 方法创建一个代理:
// As always, create a client to our dapr sidecar
// this client takes care of making sure the sidecar is started, that we can communicate, ...
const clientSidecar = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);
// Create a Proxy that allows us to use our gRPC code
const clientProxy = await clientSidecar.proxy.create<GreeterClient>(GreeterClient);
我们现在可以调用 GreeterClient 接口中定义的方法(在本例中来自 Hello World 示例)
幕后(技术工作)
- gRPC 服务在 Dapr 中启动。 我们通过
--app-port
告诉 Dapr 这个 gRPC 服务器在哪个端口上运行,并通过--app-id <APP_ID_HERE>
给它一个唯一的 Dapr 应用 ID - 我们现在可以通过将连接到 Sidecar 的客户端调用 Dapr Sidecar
- 在调用 Dapr Sidecar 时,我们提供了一个名为
dapr-app-id
的元数据键,其中包含在 Dapr 中启动的 gRPC 服务器的值(例如,我们示例中的server
) - Dapr 现在会将调用转发到配置的 gRPC 服务器
构建块
JavaScript 客户端 SDK 允许您与专注于 Client to Sidecar 功能的所有 Dapr 构建块进行交互。
调用 API
调用一个服务
import { DaprClient, HttpMethod } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const serviceAppId = "my-app-id";
const serviceMethod = "say-hello";
// POST Request
const response = await client.invoker.invoke(serviceAppId , serviceMethod , HttpMethod.POST, { hello: "world" });
// GET Request
const response = await client.invoker.invoke(serviceAppId , serviceMethod , HttpMethod.GET);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关服务调用的完整指南,请访问 How-To: Invoke a service。
状态管理 API
保存、获取和删除应用程序状态
import { DaprClient } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const serviceStoreName = "my-state-store-name";
// Save State
const response = await client.state.save(serviceStoreName, [
{
key: "first-key-name",
value: "hello"
},
{
key: "second-key-name",
value: "world"
}
]);
// Get State
const response = await client.state.get(serviceStoreName, "first-key-name");
// Get Bulk State
const response = await client.state.getBulk(serviceStoreName, ["first-key-name", "second-key-name"]);
// State Transactions
await client.state.transaction(serviceStoreName, [
{
operation: "upsert",
request: {
key: "first-key-name",
value: "new-data"
}
},
{
operation: "delete",
request: {
key: "second-key-name"
}
}
]);
// Delete State
const response = await client.state.delete(serviceStoreName, "first-key-name");
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关状态操作的完整列表,请访问 How-To: Get & save state。
https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-get-save-state/
查询状态 API
import { DaprClient } from "@dapr/dapr";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const res = await client.state.query("state-mongodb", {
filter: {
OR: [
{
EQ: { "person.org": "Dev Ops" }
},
{
"AND": [
{
"EQ": { "person.org": "Finance" }
},
{
"IN": { "state": ["CA", "WA"] }
}
]
}
]
},
sort: [
{
key: "state",
order: "DESC"
}
],
page: {
limit: 10
}
});
console.log(res);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
发布订阅 API
发布消息
import { DaprClient } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const pubSubName = "my-pubsub-name";
const topic = "topic-a";
const message = { hello: "world" }
// Publish Message to Topic
const response = await client.pubsub.publish(pubSubName, topic, message);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
订阅消息
import { DaprServer } from "@dapr/dapr";
const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "
async function start() {
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);
const pubSubName = "my-pubsub-name";
const topic = "topic-a";
// Configure Subscriber for a Topic
await server.pubsub.subscribe(pubSubName, topic, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));
await server.start();
}
有关状态操作的完整列表,请访问 How-To: Publish and subscribe。
https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/
绑定 API
调用输出绑定
import { DaprClient } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const bindingName = "my-binding-name";
const bindingOperation = "create";
const message = { hello: "world" };
const response = await client.binding.send(bindingName, bindingOperation, message);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关输出绑定的完整指南,请访问 How-To: Use bindings。
https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-bindings/
Secret API
检索 secret
import { DaprClient } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
async function start() {
const client = new DaprClient(daprHost, daprPort);
const secretStoreName = "my-secret-store";
const secretKey = "secret-key";
// Retrieve a single secret from secret store
const response = await client.secret.get(secretStoreName, secretKey);
// Retrieve all secrets from secret store
const response = await client.secret.getBulk(secretStoreName);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关 secrets 的完整指南,请访问 How-To: Retrieve Secrets。
https://docs.dapr.io/developing-applications/building-blocks/secrets/howto-secrets/
配置 API
获取配置 key
import { DaprClient } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprAppId = "example-config";
async function start() {
const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT);
const config = await client.configuration.get('config-store', ['key1', 'key2']);
console.log(config);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
Server
介绍
Dapr Server 将允许您接收来自 Dapr Sidecar 的通信并访问其面向服务器的功能,例如:订阅事件、接收输入绑定等等。
前提条件
- Dapr CLI 已安装
- 初始化 Dapr 环境
- 最新 LTS 版本的 Node 或更高版本
安装和导入 Dapr 的 JS SDK
- 使用
npm
安装 SDK:
npm i @dapr/dapr --save
- 导入库:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";
const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server
// HTTP Example
const client = new DaprClient(daprHost, daprPort);
// GRPC Example
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);
运行
要运行示例,您可以使用两种不同的协议与 Dapr sidecar 交互:HTTP(默认)或 gRPC。
使用 HTTP(默认)
import { DaprServer } from "@dapr/dapr";
const server= new DaprServer(appHost, appPort, daprHost, daprPort);
// initialize subscribtions, ... before server start
// the dapr sidecar relies on these
await server.start();
# Using dapr run
dapr run --app-id example-sdk --app-port 50051 --app-protocol http -- npm run start
# or, using npm script
npm run start:dapr-http
️ Note:这里需要 app-port,因为这是我们的服务器需要绑定的地方。 Dapr 将在完成启动之前检查应用程序是否绑定到此端口。
使用 gRPC
由于 HTTP 是默认设置,因此您必须调整通信协议以使用 gRPC。 您可以通过向客户端或服务器构造函数传递一个额外的参数来做到这一点。
import { DaprServer, CommunicationProtocol } from "@dapr/dapr";
const server = new DaprServer(appHost, appPort, daprHost, daprPort, CommunicationProtocol.GRPC);
// initialize subscribtions, ... before server start
// the dapr sidecar relies on these
await server.start();
# Using dapr run
dapr run --app-id example-sdk --app-port 50051 --app-protocol grpc -- npm run start
# or, using npm script
npm run start:dapr-grpc
️ Note:这里需要 app-port,因为这是我们的服务器需要绑定的地方。 Dapr 将在完成启动之前检查应用程序是否绑定到此端口。
构建块
JavaScript Server SDK 允许您与专注于 Sidecar 到 App 功能的所有 Dapr 构建块进行交互。
调用 API
监听调用
import { DaprServer } from "@dapr/dapr";
const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "
async function start() {
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);
await server.invoker.listen('hello-world', mock, { method: HttpMethod.GET });
// You can now invoke the service with your app id and method "hello-world"
await server.start();
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关服务调用的完整指南,请访问 How-To: Invoke a service。
发布订阅 API
订阅消息
import { DaprServer } from "@dapr/dapr";
const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "
async function start() {
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);
const pubSubName = "my-pubsub-name";
const topic = "topic-a";
// Configure Subscriber for a Topic
await server.pubsub.subscribe(pubSubName, topic, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));
await server.start();
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关状态操作的完整列表,请访问 How-To: Publish and subscribe。
https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/
绑定 API
接收一个输入绑定
import { DaprServer } from "@dapr/dapr";
const daprHost = "127.0.0.1";
const daprPort = "3500";
const serverHost = "127.0.0.1";
const serverPort = "5051";
async function start() {
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);
const bindingName = "my-binding-name";
const response = await server.binding.receive(bindingName, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));
await server.start();
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
有关输出绑定的完整指南,请访问 How-To: Use bindings。
https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-bindings/
配置 API
配置 API 目前只能通过 gRPC 使用
获取配置值
import { DaprServer } from "dapr-client";
const daprHost = "127.0.0.1";
const daprPort = "3500";
const serverHost = "127.0.0.1";
const serverPort = "5051";
async function start() {
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);
const config = await client.configuration.get("config-redis", ["myconfigkey1", "myconfigkey2"]);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
订阅 key 更改
import { DaprServer } from "dapr-client";
const daprHost = "127.0.0.1";
const daprPort = "3500";
const serverHost = "127.0.0.1";
const serverPort = "5051";
async function start() {
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);
const stream = await client.configuration.subscribeWithKeys("config-redis", ["myconfigkey1", "myconfigkey2"], () => {
// Received a key update
});
// When you are ready to stop listening, call the following
await stream.close();
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
Actors
Dapr actors 包允许您从 JavaScript 应用程序与 Dapr virtual actors 进行交互。下面的示例演示了如何使用 JavaScript SDK 与 virtual actors 进行交互。
如需更深入地了解 Dapr actors,请访问 actors 概览页面。
前提条件
- Dapr CLI 已安装
- 初始化 Dapr 环境
- 最新 LTS 版本的 Node 或更高版本
- 已安装 JavaScript NPM 包
场景
下面的代码示例粗略地描述了停车场现场监控系统的场景,可以在 Mark Russinovich 的这段视频中看到。
一个停车场由数百个停车位组成,每个停车位都包含一个传感器,可为中央监控系统提供更新。 停车位传感器(我们的 actors)检测停车位是否被占用或可用。
要自己跳入并运行此示例,请克隆源代码,该源代码可在 JavaScript SDK 示例目录中找到。
Actor 接口
Actor 接口定义了在 Actor 实现和调用 Actor 的客户端之间共享的合约。 在下面的示例中,我们为停车场传感器创建了一个接口。 每个传感器有 2 个方法:carEnter
和 carLeave
,它们定义了停车位的状态:
export default interface ParkingSensorInterface {
carEnter(): Promise<void>;
carLeave(): Promise<void>;
}
Actor 实现
Actor 实现通过扩展基本类型 AbstractActor
并实现 Actor
接口(在本例中为 ParkingSensorInterface
)来定义一个类。
下面的代码描述了一个 actor 实现以及一些辅助方法。
import { AbstractActor } from "@dapr/dapr";
import ParkingSensorInterface from "./ParkingSensorInterface";
export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
async carEnter(): Promise<void> {
// Implementation that updates state that this parking spaces is occupied.
}
async carLeave(): Promise<void> {
// Implementation that updates state that this parking spaces is available.
}
private async getInfo(): Promise<object> {
// Implementation of requesting an update from the parking space sensor.
}
/**
* @override
*/
async onActivate(): Promise<void> {
// Initialization logic called by AbstractActor.
}
}
注册 Actor
使用 DaprServer 包初始化和注册你的 actors:
import { DaprServer } from "@dapr/dapr";
import ParkingSensorImpl from "./ParkingSensorImpl";
const daprHost = "127.0.0.1";
const daprPort = "50000";
const serverHost = "127.0.0.1";
const serverPort = "50001";
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);
await server.actor.init(); // Let the server know we need actors
server.actor.registerActor(ParkingSensorImpl); // Register the actor
await server.start(); // Start the server
// To get the registered actors, you can invoke `getRegisteredActors`:
const resRegisteredActors = await server.actor.getRegisteredActors();
console.log(`Registered Actors: ${JSON.stringify(resRegisteredActors)}`);
调用 Actor 方法
注册 Actor 后,使用 ActorProxyBuilder
创建一个实现 ParkingSensorInterface
的代理对象。 您可以通过直接调用 Proxy 对象上的方法来调用 actor 方法。在内部,它转换为对 Actor API 进行网络调用并取回结果。
import { DaprClient, ActorId } from "@dapr/dapr";
import ParkingSensorImpl from "./ParkingSensorImpl";
import ParkingSensorInterface from "./ParkingSensorInterface";
const daprHost = "127.0.0.1";
const daprPort = "50000";
const client = new DaprClient(daprHost, daprPort);
// Create a new actor builder. It can be used to create multiple actors of a type.
const builder = new ActorProxyBuilder<ParkingSensorInterface>(ParkingSensorImpl, client);
// Create a new actor instance.
const actor = builder.build(new ActorId("my-actor"));
// Or alternatively, use a random ID
// const actor = builder.build(ActorId.createRandomId());
// Invoke the method.
await actor.carEnter();
将状态与 Actor 一起使用
// ...
const PARKING_SENSOR_PARKED_STATE_NAME = "parking-sensor-parked"
const actor = builder.build(new ActorId("my-actor"))
// SET state
await actor.getStateManager().setState(PARKING_SENSOR_PARKED_STATE_NAME, true);
// GET state
const value = await actor.getStateManager().getState(PARKING_SENSOR_PARKED_STATE_NAME);
if (!value) {
console.log(`Received: ${value}!`);
}
// DELETE state
await actor.removeState(PARKING_SENSOR_PARKED_STATE_NAME);
...
Actor 计时器和提醒器
JS SDK 支持 actors 可以通过注册 timers 或 reminders 来为自己安排定期工作。timers 和 reminders 之间的主要区别在于,Dapr actor runtime 在停用后不保留有关 timers 的任何信息,而是使用 Dapr actor state provider 保留 reminders 信息。
这种区别允许用户在轻量级但无状态的 timers 和更需要资源但有状态的 reminders 之间进行权衡。
Timers 和 reminders 的调度界面是相同的。要更深入地了解调度配置,请参阅 actors timers 和 reminders 文档。
Actor Timers
// ...
const actor = builder.build(new ActorId("my-actor"));
// Register a timer
await actor.registerActorTimer(
"timer-id", // Unique name of the timer.
"cb-method", // Callback method to execute when timer is fired.
Temporal.Duration.from({ seconds: 2 }), // DueTime
Temporal.Duration.from({ seconds: 1 }), // Period
Temporal.Duration.from({ seconds: 1 }), // TTL
50 // State to be sent to timer callback.
);
// Delete the timer
await actor.unregisterActorTimer("timer-id");
Actor Reminders
// ...
const actor = builder.build(new ActorId("my-actor"));
// Register a reminder, it has a default callback: `receiveReminder`
await actor.registerActorReminder(
"reminder-id", // Unique name of the reminder.
Temporal.Duration.from({ seconds: 2 }), // DueTime
Temporal.Duration.from({ seconds: 1 }), // Period
Temporal.Duration.from({ seconds: 1 }), // TTL
100 // State to be sent to reminder callback.
);
// Delete the reminder
await actor.unregisterActorReminder("reminder-id");
要处理回调,您需要覆盖 actor 中的默认 receiveReminder
实现。 例如,从我们最初的 actor 实现中:
export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
// ...
/**
* @override
*/
async receiveReminder(state: any): Promise<void> {
// handle stuff here
}
// ...
}
有关 actors 的完整指南,请访问 How-To: Use virtual actors in Dapr。
Logging
介绍
JavaScript SDK 带有一个开箱即用的基于 Console
的 logger。SDK 发出各种内部日志,以帮助用户了解事件链并解决问题。此 SDK 的使用者可以自定义日志的详细程度,并为 logger 提供自己的实现。
配置日志级别
有五个级别的日志记录,按重要性降序排列 - error
、warn
、info
、verbose
和 debug
。 将日志设置为一个级别意味着 logger 将发出至少与上述级别一样重要的所有日志。 例如,设置为 verbose
日志意味着 SDK 不会发出 debug
级别的日志。默认日志级别是 info
。
Dapr Client
import { CommunicationProtocolEnum, DaprClient, LogLevel } from "@dapr/dapr";
// create a client instance with log level set to verbose.
const client = new DaprClient(
daprHost,
daprPort,
CommunicationProtocolEnum.HTTP,
{ logger: { level: LogLevel.Verbose } });
有关如何使用 Client 的更多详细信息,请参阅 JavaScript Client。
https://docs.dapr.io/developing-applications/sdks/js/js-client/
DaprServer
import { CommunicationProtocolEnum, DaprServer, LogLevel } from "@dapr/dapr";
// create a server instance with log level set to error.
const server = new DaprServer(
serverHost,
serverPort,
daprHost,
daprPort,
CommunicationProtocolEnum.HTTP,
{ logger: { level: LogLevel.Error } });
有关如何使用 Server 的更多详细信息,请参阅 JavaScript Server。
https://docs.dapr.io/developing-applications/sdks/js/js-server/
自定义 LoggerService
JavaScript SDK 使用内置 Console
进行日志记录。要使用 Winston 或 Pino 等自定义 logger,您可以实现 LoggerService
接口。
基于 Winston 的日志记录:
创建 LoggerService
的新实现。
import { LoggerService } from "@dapr/dapr";
import * as winston from 'winston';
export class WinstonLoggerService implements LoggerService {
private logger;
constructor() {
this.logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'combined.log' })
]
});
}
error(message: any, ...optionalParams: any[]): void {
this.logger.error(message, ...optionalParams)
}
warn(message: any, ...optionalParams: any[]): void {
this.logger.warn(message, ...optionalParams)
}
info(message: any, ...optionalParams: any[]): void {
this.logger.info(message, ...optionalParams)
}
verbose(message: any, ...optionalParams: any[]): void {
this.logger.verbose(message, ...optionalParams)
}
debug(message: any, ...optionalParams: any[]): void {
this.logger.debug(message, ...optionalParams)
}
}
将新实现传递给 SDK。
import { CommunicationProtocolEnum, DaprClient, LogLevel } from "@dapr/dapr";
import { WinstonLoggerService } from "./WinstonLoggerService";
const winstonLoggerService = new WinstonLoggerService();
// create a client instance with log level set to verbose and logger service as winston.
const client = new DaprClient(
daprHost,
daprPort,
CommunicationProtocolEnum.HTTP,
{ logger: { level: LogLevel.Verbose, service: winstonLoggerService } });
官方示例代码库
NodeJS & Dapr Javascript SDK 官方使用指南的更多相关文章
- Sentry 官方 JavaScript SDK 简介与调试指南
系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...
- 华为云函数中使用云数据库的JavaScript SDK基础入门
背景介绍 使用云数据库Server端的SDK,此处我以华为提供的官方Demo为例,他们的Demo也已经开源放在了GitHub上,大家需要的可以自行下载. https://github.com/AppG ...
- Hello.js – Web 服务授权的 JavaScript SDK
Hello.js 是一个客户端的 Javascript SDK,用于实现 OAuth2 认证(或者基于 OAuth 代理实现的 OAuth1)的 Web 服务和查询 REST API. HelloJS ...
- JavaScript 原型的深入指南
摘要: 理解prototype. 原文:JavaScript 原型的深入指南 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 不学会怎么处理对象,你在 JavaScript 道路就就走 ...
- Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT SDK 配置详解
系列 Sentry-Go SDK 中文实践指南 一起来刷 Sentry For Go 官方文档之 Enriching Events Snuba:Sentry 新的搜索基础设施(基于 ClickHous ...
- Quartz.net官方开发指南系列篇
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
- 遇见NodeJS:JavaScript的贵人
在大家的印象中,相当长一段时间里,JavaScript是一门脚本语言,一般不能成为某个项目的担纲主角,作用只是在浏览器里帮忙校验校验输入是不是正确,响应一下鼠标.键盘事件,或者让某个HTML元素动起来 ...
- 微信小程序wx.getLocation()获取经纬度及JavaScript SDK调用腾讯地图API获取某一类地址
简介 腾讯位置服务为微信小程序提供了基础的标点能力.线和圆的绘制接口等地图组件和位置展示.地图选点等地图API位置服务能力支持,使得开发者可以自由地实现自己的微信小程序产品. 在此基础上,腾讯位置服务 ...
- NodeJS,JavaScript正在吞噬这个世界
NodeJS,JavaScript正在吞噬这个世界 NodeJS,一个基于Google Chrome V8 JS引擎的服务器端JavaScript运行时,曾经被认识只是一个赶时髦的技术,有谁会想到,它 ...
随机推荐
- vue大型电商项目尚品汇(前台篇)day05终结篇
前台部分到此结束,一路走来还挺怀念,今天主要是对整个项目的完成做一个最后的收尾工作,对于功能上的需求没有什么了,主要就是项目上线的一些注意事项. 一.个人中心二级路由 当我们点击查看订单应该跳转到个人 ...
- Java ES 实现or查询
es mapping里有三个字段: A:Integer B:Integer C:TEXT 现在想实现一个查询,来检索 ( (A =1 and B=2) or (c like "test ...
- 高度灵活可定制的PC布局:头部按钮、左边栏、右边栏、状态栏
什么是自适应布局 CabloyJS提供了一套布局管理器,实现自适应布局 关于自适应布局的概念,强烈建议先阅读以下两篇文章: 自适应布局:pc = mobile + pad 自适应布局:视图尺寸 什么是 ...
- .NET C#基础(6):命名空间 - 有名字的作用域
0. 文章目的 面向C#新学者,介绍命名空间(namespace)的概念以及C#中的命名空间的相关内容. 1. 阅读基础 理解C与C#语言的基础语法. 理解作用域概念. 2. 名称冲突与命 ...
- 在Winform开发中,使用Async-Awati异步任务处理代替BackgroundWorker
在Winform开发中有时候我们为了不影响主UI线程的处理,以前我们使用后台线程BackgroundWorker来处理一些任务操作,不过随着异步处理提供的便利性,我们可以使用Async-Awati异步 ...
- ExtJS配置TabPanel可以拖拽Tab标签页
1.环境说明 ExtJS版本:7.4.0.42 Sencha Cmd: v7.5.1.20 开发工具:WebStorm 2022.1.1 PS:如果是老版本的ExtJS,引入Ext.ux.TabReo ...
- JS数组at函数(获取最后一个元素的方法)介绍
本文介绍js中数组的at函数,属于比较简单的知识普及性文章,难度不大. 0x00 首先,我们可以思考如下一个问题,如果要获取一个数组的最后一个元素(这是很常用的操作),我们应该怎么做? 相信大部分人能 ...
- rosbag遍历数据出错:(unicode error) 'utf-8' codec can't decode byte 0xcd in position 31: invalid continuation byte
主题: 前言 针对ros系统记录的bag文件,可以使用python的rosbag包,按照不同起止时间和topic进行提取. 然而,有的topic可以使用rosbag读取,但是不能遍历,存在解码错误.原 ...
- Sublime Text 3 如何清除上次打开文件记录
打开顶部菜单栏:进入 Preferences => Settings-User修改如下: {"hot_exit": false,"remember_open_fil ...
- nodeJS与MySQL实现分页数据以及倒序数据
大家在做项目时肯定会遇到列表类的数据,如果在前台一下子展示,速度肯定很慢,那么我们可以分页展示,比如说100条数据,每10条一页,在需要的时候加载一页,这样速度肯定会变快了.那么这里我给大家介绍如何在 ...