Elasticsearch를 사용하여 Node.js로 로깅 서버 구축하기

Hoon·2019년 12월 22일
41
post-thumbnail

어느 날 회사에서 Elasticsearch를 사용해 로그를 쌓는 모습을 보게되었는데 어떻게 로그를 쌓는건지 궁금하기도 했고 Elasticsearch가 정확히 무엇인지 몰라서 Elasticseach에 대해서 학습하기 시작했습니다. ~( 사실은 로그를 쌓고 Kibana로 시각화하는 모습이 너무 멋있어서 학습 시작했습니다 )~

이 글은 Elasticsearch이 무엇인지 모르는 개발자를 대상으로 Elasticsearch에 대한 기초적인 개념을 담고 있습니다. Elasticsearch에 개념과 특징을 이해를 한 후 Elasticsearch를 사용하여 Node.js로 로깅 서버를 만들어 보도록 하겠습니다. 또한 더 나아가 데이터 시각화까지 해보도록 하겠습니다.

실습에 사용된 코드는 https://github.com/jeffchoi72/general-server/tree/elasticsearch-tutorial 에서 확인 할 수 있습니다.

Elasticsearch 개념과 특징

Elasticsearch를 정의하면 모든 유형( 정형, 비정형)의 데이터를 처리할 수 있는 분산형 오픈 소스 검색 및 분석 엔진입니다. 분산형이란 서버 한 대로 요청을 처리하는게 아닌 여러대의 서버로 요청을 처리하는 것을 의미합니다.

또한, Elasticsearch는 REST API를 제공하여 데이터를 처리하며, 분산형 특징으로인해 트래픽에 따라 성능을 쉽게 스케일업 할 수 있습니다.

또한, ELK 스택이라고 Elasticsearch ( 검색 및 분석 엔진 ), Logstash ( 데이터 집계 및 처리 ), Kibana ( 데이터 시각화 ) 툴들을 사용하여 데이터 분석 및 로깅, 검색엔진을 쉽게 만들 수 있습니다.

참고로 Elasticsearch를 줄여서 ES라고 불릅니다.

Elasticsearch 에서 데이터를 저장하는 방법

Elasticsearch에서 데이터를 저장할때 index 단위로 저장합니다.

예를들어, 서버 API 관련된 로그들을 server_api_logs 이름에 index를 만들어 관련된 데이터를 저장하합니다. 추가적으로 index에 저장되는 데이터들은 포맷을 정의할 수 있습니다.

Elasticsearch는 어떻게 사용될 수 있을까?

Elasticsearch는 띄어난 확장성과 빠른 속도로 아래와 같은 곳에서 사용 할 수 있습니다.

  • 검색 시스템
  • 로깅과 로그 분석 시스템
  • 인프라 메트릭과 컨테이너 모니터링
  • 애플리케이션 성능 모니터링
  • 위치 기반 정보 데이터 분석 및 시각화
  • 데이터 분석

Elasticsearch에서 제공하는 기능들이 많고 Elasticsearch에 적용할 수 있는 툴들이 다양해서 데이터 관련된건 다 할 수 있을거 같다는 생각이 듭니다.

Elasticsearch 설치방법

Elasticsearch를 설치하기 위해 어떤 방식들이 있는지 알아보도록 하겠습니다.

1. Elasticsearch를 직접 설치한다.

2. Amazon Elasticsearch Service를 사용한다

3. 찾아보면 나오는 다양한 클라우드 서비스

Elasticsearch 설치하기

이제 실습을 위해 Elasticsearch를 설치하도록 하겠습니다.

해당 실습에서는 Docker를 사용해 Elasticsearch를 설치합니다. docker를 설치 안하셨다면 초보를 위한 도커 안내서 - 설치하고 컨테이너 실행하기 글을 보고 도커를 설치해주세요.

실습을 따라하는데 Docker를 몰라도 크게 지장은 없지만 Docker가 무엇인지 정확히 개념을 잡고 시작하고 싶다면 초보를 위한 도커 안내서 - 도커란 무엇인가? 글을 통해 도커 개념을 학습해보세요! ( 강추 )

도커를 다 설치하셨다면 다음과 같은 명령어로 elasticsearch를 실행해줍니다.
docker run -d --name elasticsearch-test-01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.5.0

간단하게 해석하자면, elasticsearch7.5 버전 이미지를 설치해 elasticsearch-test-01 이란 이름에 컨테이너로 실행한다 라는 의미입니다.

깨알 옵션 정리

  • --name: 컨테이너 이름 설정
  • -p: 호스트와 컨테이너의 포트를 연결할때 쓰이는 명령어 ( 왼쪽 호스트, 오른쪽 컨테이너 )
  • -d: 백그라운드 실행 모드
  • -e: 환경변수 설정

아래와 같이 docker ps를 했을 때 elasticsearch-test-01이란 이름으로 컨테이너가 보이면 현재 실행중인거고

http://localhost:9200 으로 접속했을때 아래처럼 보인다면 실습 준비가 끝났습니다.

Elasticsearch에 restful api로 데이터 저장방식 이해하기

Elasticsearch를 사용할려면 Elasticsearch에서 제공하는 Restful API를 사용해야합니다.
Elasticsearch와 Node.js를 사용해 로깅 서버를 만들어보기전 간단하게 Elasticsearch에서 제공하는 API를 사용하여 Elasticsearch에서는 어떻게 데이터를 저장하고 있는지 이해해보도록 하겠습니다.

실습은 postman ( https://www.getpostman.com ) 을 사용해서 하겠습니다.

index 생성하기

Elasticsearch에 users 라는 index를 생성하도록 하겠습니다.

PUT method로 http://127.0.0.1:9200/users 주소로 요청해주세요.

index 조회하기

GET 방식으로 http://127.0.0.1:9200/users 에 요청해주세요.

그 후 아래와 같이 응답리스폰스에서 index 정보를 볼 수 있습니다.

document 생성하기

document는 생성된 index에 써지는 데이터 row를 의미합니다.

POST 방식으로 http://127.0.0.1:9200/users/_doc 주소에 아래 body 내용과 함께 요청해 줍니다.

{
  "name" : "younghoon"
}

document 조회하기

이제 생성한 document를 조회하도록 하겠습니다.

GET 방식으로 http://127.0.0.1:9200/users/_search 주소에 요청하면 생성한 document 들을 리턴합니다.

다시한번 정리하자면 elasticsearch 에서는 데이터를 저장할때 데이터들이 어떤 곳에 저장할지 지정해주는게 index입니다. 또한 index에 저장되는 데이터들을 document라고 부릅니다.

나머지 개념들도 실습을 하면서 더 자세히 이해해보도록 하겠습니다.

만약, Elasticsearch를 제어하는 다양한 api를 보고 싶다면 Elasticsearch restful api document 에서 확인할 수 있습니다.

Elasticsearch로 API 요청 정보와 에러 정보 로깅하기

실습 환경 준비하기

Node.js, Typescript, Koa를 사용합니다.
Typescript와 Koa를 잘 몰라도 Elasticsearch 개념을 이해하는데는 큰 문제가 없습니다.

아래처럼 git clone https://github.com/jeffchoi72/general-server.git명령어로 git clone을 해주세요.

그 후 yarn && yarn dev를 해주세요.

http://localhost:4000 접속해 아래처럼 보이면 실습 환경 준비가 모두 끝났습니다.

Elasticsearch 라이브러리 설치하기

node에서 elasticsearch를 사용할 수 있게 elasticsearch에서 만든 @elastic/elasticsearch 라이브러리가 있습니다.

yarn add @elastic/elasticsearch 명령어로 해당 라이브러리를 설치해줍니다.

Node.js에서 Elasticsearch로 데이터 보내보기

API 요청 정보를 보내기전 Elasticsearch로 데이터를 한번 보내보도록 하겠습니다.

src/server.ts로 이동해서 아래와 같은 코드를 작성해 줍니다.

import { Client } from '@elastic/elasticsearch';
import Koa from 'koa';
import BodyParser from 'koa-bodyparser';
import kcors from 'koa-cors';
import Router from 'koa-router';
import Container from 'typedi';

import { Routes } from './controllers';

// Elasticsearch를 사용하기 위해 객체를 생성합니다.
const client = new Client({
  node: "http://localhost:9200/"
});

class Server {
  private app: Koa;
  private router: Router;

  constructor() {
    this.app = new Koa();
    this.router = new Router();

    this.setMiddlewares();
    this.setRoutes();
  }

  private setMiddlewares() {
    this.app.use(kcors());
    this.app.use(BodyParser());
  }

  private setRoutes() {
    this.router.use("/api", Container.get(Routes).getRoutes());

    this.app.use(this.router.routes());
    this.app.use(this.router.allowedMethods());
  }

  public async run(port: string = "4000"): Promise<void> {
    this.app.listen(port);

    console.log(`Server application is up and running on port ${port}`);

    // Elasticsearch 객체에서는 index 라는 메소드를 사용해 elasticsearch로 데이터를 보낼 수 있습니다.
    await client.index({
      index: "boot-logs",
      body: {
        message: `Server application is up and running on port ${port}`,
        timestamp: new Date()
      }
    });
  }
}

export default Server;
client.index({
  index: "boot-logs",
  body: {
    message: `Server application is up and running on port ${port}`,
    timestamp: new Date()
  }
})

위에 코드를 해석해보면 boot-logs 라는 이름으로 index를 지정해주었고 body 객체 안에 document field인 message와 timestamp를 정의해준 것을 볼 수 있습니다.

데이터가 잘 들어갔는지 확인해 보겠습니다.

GET 방식으로 http://127.0.0.1:9200/boot-logs/_search 주소로 요청해주세요.

아래처럼 응답 리스폰스에 데이터가 추가된 모습을 볼 수 있습니다.

이제 Elasticseearch을 사용해 데이터를 보내는 로직을 더 추상화해서 api 요청 정보와 에러 정보를 기록해보도록 하겠습니다,

Elasticsearch를 사용해 데이터를 보내는 로직 추상화하기

API 요청 정보와 에러 정보를 수행하는 코드를 간결하게 작성하기 위해 Elasticsearch를 사용해서 데이터를 보내는 로직을 추상화하도록 하겠습니다.

/src/libries/elasticSearch.ts를 만들어주고 아래와 같은 코드를 넣어주세요

import { Client, RequestParams } from '@elastic/elasticsearch';

const client = new Client({
  node: "http://localhost:9200"
});

export abstract class ElasticSearch<T> {
  protected readonly INDEX_NAME: string;

  constructor(indexName: string) {
    this.INDEX_NAME = indexName;
  }

  protected async requestElasticSearch(bodyData: RequestParams.Index) {
    await client.index(bodyData);
  }

  public abstract async putLog(log: T): Promise<void>;
}

위에 코드는 ElasticSearch에 커넥션을 맺어 요청하는 로직을 제공하는 클래스입니다.

ElasticSearch 클래스를 상속 받게해 API 요청 정보를 로깅하는 것과 에러 정보를 로깅하는 코드를 만들어보겠습니다.

/src/libries 디렉터리를 만들어주고 아래와 같은 코드들을 작성해주세요

/src/libries/elasticSearchAPILog.ts

import { RequestParams } from '@elastic/elasticsearch';

import { ElasticSearch } from './elasticSearch';

const INDEX_NAME = "server_api_logs";

export type ElasticSearchAPILogType = {
  apiName: string;
  method: string;
  url: string;
  header: Object;
  errorMessage?: string;
};

export class ElasticSearchAPILog extends ElasticSearch<
  ElasticSearchAPILogType
> {
  constructor() {
    super(INDEX_NAME);
  }

  public async putLog(log: ElasticSearchAPILogType): Promise<void> {
    try {
      const bodyData: RequestParams.Index = {
        index: this.INDEX_NAME,
        body: {
          ...log,
          timestamp: new Date()
        }
      };

      await this.requestElasticSearch(bodyData);

      console.log("[SUCCESS]: ElasticSearchAPILog putLog method");
    } catch (error) {
      console.log(
        `[ERROR]:  ElasticSearchAPILog putLog method, error-message=${error.message}`
      );
      return;
    }
  }
}

src/libries/elasticSearchErrorLog.ts

import { RequestParams } from '@elastic/elasticsearch';
import { Service } from 'typedi';

import { ElasticSearch } from './elasticSearch';

const INDEX_NAME = "server_error_logs";

export type ElasticSearchErrorLogType = {
  apiName: string;
  method: string;
  url: string;
  header: Object;
  message: string;
};

@Service()
export class ElasticSearchErrorLog extends ElasticSearch<
  ElasticSearchErrorLogType
> {
  constructor() {
    super(INDEX_NAME);
  }

  public async putLog(log: ElasticSearchErrorLogType) {
    try {
      const bodyData: RequestParams.Index = {
        index: this.INDEX_NAME,
        body: {
          ...log,
          timestamp: new Date()
        }
      };

      await this.requestElasticSearch(bodyData);

      console.log("[SUCCESS]: ElasticSerachErrorLog putLog method");
    } catch (error) {
      console.log(
        `[ERROR]: ElasticSearchErrorLog putLog method, error-message=${error.message}`
      );
      return;
    }
  }
}

src/libries/index.ts

export * from "./elasticSearchAPILog";
export * from "./elasticSearchErrorLog";

ElasticSearch에 데이터를 보내는 공통된 로직을 만들고 해당 로직을 상속해서 의도에 맞는 코드들을 작성했습니다.

이제 모든 로깅을 할때 사용되는 역할을 수행하는 LogService를 만들어 보도록 하겠습니다.

src/services/logService.ts를 만들어 아래와 같은 코드를 작성해주세요.

import { Service } from 'typedi';

import { ElasticSearchAPILog, ElasticSearchAPILogType } from '../libraries/elasticSearchAPILog';
import { ElasticSearchErrorLog, ElasticSearchErrorLogType } from '../libraries/elasticSearchErrorLog';

@Service()
export class LogService {
  constructor(
    private elasticSearchErrorLog: ElasticSearchErrorLog,
    private elasticSearchAPILog: ElasticSearchAPILog
  ) {}

  public log(data: ElasticSearchAPILogType) {
    this.elasticSearchAPILog.putLog(data);
  }

  public error(data: ElasticSearchErrorLogType) {
    this.elasticSearchErrorLog.putLog(data);
  }
}

src/services/index.ts

export * from "./LogService";

해당 코드에 의도를 해석하자면 로깅하는걸 log와 error로 추상화 시켜 사용하는 입장에서 elasticSearch를 사용하는걸 모르게 했습니다.

이렇게 하면 좋은점이 나중에 elasticSearch를 안쓴다면 쉽게 걷어낼 수 있고 새로운 로깅 서비스를 붙인다고하면 쉽게 붙일 수 있습니다!

왜냐하면 모든 로깅은 LogService를 사용해 로깅을 하니까!

다시 본론으로 돌아와, 이제 로그를 찍을 수 있는 모든 준비를 끝냈습니다.

이제 API 요청 정보를 로깅해보도록 하겠습니다.

ElaticSearch로 API 요청 데이터 로깅하기

src/server.ts에 아래와 같이 setRoutes 부분에 로깅 코드를 추가해줍니다.

import Koa, { Context } from 'koa';
import BodyParser from 'koa-bodyparser';
import kcors from 'koa-cors';
import Router from 'koa-router';
import Container from 'typedi';

import { Routes } from './controllers';
import { LogService } from './services';

class Server {
  private app: Koa;
  private router: Router;

  constructor() {
    this.app = new Koa();
    this.router = new Router();

    this.setMiddlewares();
    this.setRoutes();
  }

  private setMiddlewares() {
    this.app.use(kcors());
    this.app.use(BodyParser());
  }

  private setRoutes() {
    this.router.use("/api", Container.get(Routes).getRoutes());

    this.app.use((ctx: Context, next: Koa.Next) => {
      // DI에서 LogService 객체를 꺼낸다.
      const logService = Container.get(LogService);

      const { method, url, header } = ctx.request;

      logService.log({
        method, // 요청 method 기록 ( ex: GET, POST, PUT, DELETE )
        url, // 요청 url 기록
        header, // 요청 헤더 기록
        apiName: `[${method}]-${url}` // method - url 조합으로 apiName을 기록
      });

      next();
    });

    this.app.use(this.router.routes());
    this.app.use(this.router.allowedMethods());
  }

  public async run(port: string = "4000"): Promise<void> {
    this.app.listen(port);

    console.log(`Server application is up and running on port ${port}`);
  }
}

export default Server;

그 후 아래와 같이 GET 방식으로 localhost:4000/api/posts/2 주소로 요청을 보냅니다.

서버가 실행된 터미널을 보면 아래와 같이 로그가 찍히면 성공입니다.

그리고 ElasticSearch index 데이터를 가지고오는 API인 http://127.0.0.1:9200/server_api_logs/_search 을 GET 방식으로 호출해주면 아래와 같은 데이터들이 쌓인 모습을 볼 수 있습니다.

ElasticSearch로 API 에러 정보 로깅하기

이제 API 에러 정보를 로깅해보도록 하겠습니다.

실습을 위해 에러를 반환하는 API를 만들어 보겠습니다.

아래 코드들을 작성해 주세요.

src/controllers/auth.controller.ts

import { Context } from 'koa';
import { Service } from 'typedi';

import { LogService } from '../services';

@Service()
export class AuthController {
  constructor(private logService: LogService) {}

  public signup = async (ctx: Context) => {
    const { userId, password, userName } = ctx.request.body;

    try {
      throw new Error("Test Error");

      // ... 회원가입을 로직을 탄다. ( 생략 )

      ctx.status = 200;
      ctx.body = {
        message: "success",
        user: {
          id: userId,
          name: userName
        }
      };
      return;
    } catch (error) {
      const { method, url, header } = ctx.request;

      this.logService.error({
        method,
        url,
        header,
        apiName: `[${method}]-${url}`,
        message: error.message
      });

      ctx.status = 500;
      ctx.body = {
        message: "server-error"
      };
      return;
    }
  };
}

src/controllers/index.ts

import 'reflect-metadata';

import { Context } from 'koa';
import Router from 'koa-router';
import { Service } from 'typedi';

import { AuthController } from './auth.controller';
import { PostController } from './post.controller';

@Service()
export class Routes {
  private router: Router;

  constructor(
    private postController: PostController,
    private authController: AuthController
  ) {
    this.router = new Router();
    this.setRoutes();
  }

  private setRoutes() {
    this.router.get("/posts", this.postController.getPosts);
    this.router.get("/posts/:id", this.postController.getPost);
    this.router.post("/auth/signup", this.authController.signup);

    this.router.all("*", (ctx: Context) => {
      ctx.status = 404;
      ctx.body = "존재하지 않는 API 입니다";
    });
  }

  public getRoutes() {
    return this.router.routes();
  }
}

POST 방식으로 아래와 같이 http://127.0.0.1:4000/api/auth/signup 주소에 body 데이터를 작성한채 요청해주세요.

그 후 응답 리스폰스로 아래와 같이 반환을 받고

{
    "message": "server-error"
}

서버 터미널에는 아래와 같이 찍힙니다.

GET 방식으로 http://127.0.0.1:9200/server_error_logs/_search 주소로 요청을 하면 아래와 같이 로그가 잘 쌓인 모습을 확인 할 수 있습니다.

Kibana로 데이터 시각화 하기

Kibana 라는 툴로 ElasticSearch에 쌓은 데이터들을 시각화 할 수 있는데요!
ElasticSearch에 API 요청 정보와 Error 로그 데이터들을 쌓았습니다.

Kibana 설치하기

먼저, docker로 kibana를 설치해보도록 하겠습니다.

다음과 같은 명령어를 사용해 kibana 7.5.1 버전을 설치해 주세요.

docker pull docker.elastic.co/kibana/kibana:7.5.1

그 후 도커로 설치한 kibana를 실행해줍니다.

docker run  -d --link :elasticsearch-container-id:elasticsearch -p 5601:5601 kibana:7.5.1

:elasticsearch-container-id에 들어가는 container id는 docker ps 명령어를 통해서 볼 수 있습니다.

docker image가 elasticsearch인 container id를 넣어주세요.

그 후 kibana를 실행했다면 http://localhost:5601 으로 접속해주세요.

바로 접속하면 부트 시간때문에 안될 수 있음! 그럴땐 당황하지 말고 새로고침을 해주세요.

아래와 같은 화면이 뜨면 성공입니다.

Kibana에 Elasticsearch Index 등록하기

아래와 같이 저희는 Sample Data가 필요하지 않기 때문에 Explore on my own 버튼을 눌러주세요.

왼쪽 맨 아래 끝에 있는 아이콘을 누르고 Elasticsearch > Index Management 을 눌러주세요.

그러면 현재 Elasticsearch에 등록된 index 들을 볼 수 있습니다.

kibana에서 시각화를 하려면 Elasticsearch에 등록되어 있는 Index 패턴을 등록해줘야하는데요.

해당 페이지에 Kibana > Index Patterns를 눌러주세요.
그리고 Create index pattern 버튼을 눌러주세요.

server_api_logs index를 kibana에 등록하겠습니다.

index pattern에 server_api_logs를 입력해주세요.

아래처럼 index pattern에 시간 필터에 timestamp를 선택해 주세요.
그 다음 Create index pattern 버튼을 클릭해 index를 만들어주세요.

아래와 같이 kibana에 server_api_logs index pattern이 추가된걸 볼 수 있습니다.

이제 server_api_logs index에 저장된 documents들을 kibana에서 보도록 하겠습니다.

왼쪽 사이드 네비게이션에 시계 아이콘 ( Discover )를 눌러주세요.
만약, 아래처럼 로그가 안보인다면 시간을 Last 15 days로 바꿔주세요.

아래처럼 Elasticsearch에 저장되어 있는 로그들을 kibana에서 볼 수 있습니다.

이제 kibana로 어떤 API가 가장 많이 호출되는지 시각화를 해보도록 하겠습니다.

Visualize를 눌러서 Create new Visualization 버튼을 눌러주세요.

어떻게 시각화를 할건지 선택하는건데 Horizontal Bar를 선택하도록 하겠습니다.

Metrics는 Y축에 들어갈 값으로 Count,Max Median, Min 등 계산할 수 있는 연산자들을 선택할 수 있습니다.

Metrics에 default로 Count가 설정되어 있어 수정할 필요가 없습니다.

그 다음 Buckets 눌러 다음과 같은 값들을 넣어주세요.
그 다음 Buckets을 눌러 aggreagtion 할 요소를 Terms로 선택한 다음 차트에 보여질 필드는 apiName.keyword를 선택해주세요.

Buckets에는 X축에 들어갈 값들을 설정하는 항목인데요.
Aggregation Terms는 특정 필드를 그룹화한 데이터를 의미합니다.

Aggregation에다양한 옵션이 궁금하다면 https://www.elastic.co/guide/en/elasticsearch/reference/7.5/search-aggregations-bucket.html 사이트에서 볼 수 있습니다.

그 후 맨 위에 화살표 버튼을 누르시면 다음과 같이 그래프가 업데이트 되는것을 볼 수 있습니다.

Kibana로 대시보드 만들기

Kibana에서는 시각화한 그래프들을 모아두는 대시보드를 만들 수 있는데요! 만들어보도록 하겠습니다.

먼저, API를 요청 횟수를 기록한 그래프를 저장하도록 하겠습니다.

그 다음 왼쪽 사이드 네비게이션에서 Dashboard를 눌러줍니다.

dashboard 페이지에서 Add 버튼을 누른 후 API Call Count 그래프를 추가해줍니다.

대시보드가 완성됬습니다!

이제 server_error_logs index도 위에 과정을 기억하면서 데이터 시각화를 해보세요!

마무리

Elasticsearch가 무엇인지 어떻게 사용하는지 몰라 학습하면서 배운 것들을 글로 정리했습니다. 처음 배우는 입장에서 Elasticsearch가 무엇인지 어떻게 사용하는지 모르는 분들에게 많은 도움이 되었으면 좋겠네요.

제가 개념을 잘못 이해해 설명을 잘못했거나 혹은 오탈자가 있다면 댓글로 알려주세요!

실습에 사용된 코드는 https://github.com/jeffchoi72/general-server/tree/elasticsearch-tutorial 에서 확인 할 수 있습니다.

profile
Software Engineer

9개의 댓글

comment-user-thumbnail
2019년 12월 24일

wow 갓갓.. 다음에 시간날때 따라해 보겠습니다. 감사합니다.

1개의 답글
comment-user-thumbnail
2019년 12월 30일

매번 유익한 글 너무 감사합니다 ㅎㅎ

1개의 답글
comment-user-thumbnail
2020년 3월 29일

믓쪄요

1개의 답글
comment-user-thumbnail
2020년 7월 1일

도움이 많이 됐어요. 감사합니다!

답글 달기
comment-user-thumbnail
2020년 7월 20일

좋은 글 감사합니다ㅎㅎ. 저희 회사에서도 kibana로 로깅을 하고 있는데 프로그램이 꽤 무거운 걸로 기억합니다. 혹시 어떤 스펙의 인스턴스을 사용하셨고 비용은 어느정도 나왔는지 알 수 있을까요?

답글 달기
comment-user-thumbnail
2020년 12월 29일

services/logService.ts에
import { ElasticSearchAPILog, ElasticSearchAPILogType } from '../libries/elasticSearchAPILog';
import { ElasticSearchErrorLog, ElasticSearchErrorLogType } from '../libries/elasticSearchErrorLog';

경로 오타가 있습니다. ^^

답글 달기