Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/logx/readme-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# logx

[English](readme.md) | 简体中文
[English](readme.md) | 简体中文 | [한국어](readme-ko.md)

## logx 配置

Expand Down
205 changes: 205 additions & 0 deletions core/logx/readme-ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<img align="right" width="150px" src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png">

# logx

[English](readme.md) | [简体中文](readme-cn.md) | 한국어

## logx 설정

```go
type LogConf struct {
ServiceName string `json:",optional"`
Mode string `json:",default=console,options=[console,file,volume]"`
Encoding string `json:",default=json,options=[json,plain]"`
TimeFormat string `json:",optional"`
Path string `json:",default=logs"`
Level string `json:",default=info,options=[info,error,severe]"`
Compress bool `json:",optional"`
KeepDays int `json:",optional"`
StackCooldownMillis int `json:",default=100"`
MaxBackups int `json:",default=0"`
MaxSize int `json:",default=0"`
Rotation string `json:",default=daily,options=[daily,size]"`
}
```

- `ServiceName`: 서비스 이름을 설정합니다. 선택 사항입니다. `volume` 모드에서는 이 이름이 로그 파일 생성에 사용됩니다. `rest/zrpc` 서비스에서는 이름이 `rest` 또는 `zrpc`의 이름으로 자동 설정됩니다.
- `Mode`: 로그 출력 모드입니다. 기본값은 `console`입니다.
- `console` 모드는 로그를 `stdout/stderr`에 씁니다.
- `file` 모드는 `Path`로 지정한 파일에 로그를 씁니다.
- `volume` 모드는 docker에서 사용하며, 마운트된 볼륨에 로그를 씁니다.
- `Encoding`: 로그 인코딩 방식을 나타냅니다. 기본값은 `json`입니다.
- `json` 모드는 로그를 json 형식으로 씁니다.
- `plain` 모드는 터미널 색상이 활성화된 일반 텍스트로 로그를 씁니다.
- `TimeFormat`: 시간 형식을 사용자 지정합니다. 선택 사항입니다. 기본값은 `2006-01-02T15:04:05.000Z07:00`입니다.
- `Path`: 로그 경로를 설정합니다. 기본값은 `logs`입니다.
- `Level`: 로그를 필터링할 로깅 레벨입니다. 기본값은 `info`입니다.
- `info`: 모든 로그가 기록됩니다.
- `error`: `info` 로그가 억제됩니다.
- `severe`: `info`와 `error` 로그가 억제되고 `severe` 로그만 기록됩니다.
- `Compress`: 로그 파일 압축 여부입니다. `file` 모드에서만 동작합니다.
- `KeepDays`: 로그 파일을 보관할 일수입니다. 지정한 일수가 지나면 오래된 파일이 자동으로 삭제됩니다. `console` 모드에는 영향을 주지 않습니다.
- `StackCooldownMillis`: 스택 트레이스를 다시 기록하기까지의 밀리초입니다. 스택 트레이스 로그 폭주를 방지하는 데 사용됩니다.
- `MaxBackups`: 보관할 백업 로그 파일 개수입니다. 0은 모든 파일을 영구 보관한다는 의미입니다. `Rotation`이 `size`일 때만 적용됩니다. 참고: `KeepDays` 옵션의 우선순위가 더 높습니다. `MaxBackups`가 0이더라도 `KeepDays` 제한에 도달하면 로그 파일은 삭제됩니다.
- `MaxSize`: 현재 기록 중인 로그 파일이 차지할 수 있는 최대 공간입니다. 0은 제한 없음을 의미합니다. 단위는 `MB`입니다. `Rotation`이 `size`일 때만 적용됩니다.
- `Rotation`: 로그 로테이션 규칙의 유형입니다. 기본값은 `daily`입니다.
- `daily`: 날짜 단위로 로그를 회전합니다.
- `size`: 로그 크기 단위로 로그를 회전합니다.

## 로깅 메서드

```go
type Logger interface {
// Error logs a message at error level.
Error(...any)
// Errorf logs a message at error level.
Errorf(string, ...any)
// Errorv logs a message at error level.
Errorv(any)
// Errorw logs a message at error level.
Errorw(string, ...LogField)
// Info logs a message at info level.
Info(...any)
// Infof logs a message at info level.
Infof(string, ...any)
// Infov logs a message at info level.
Infov(any)
// Infow logs a message at info level.
Infow(string, ...LogField)
// Slow logs a message at slow level.
Slow(...any)
// Slowf logs a message at slow level.
Slowf(string, ...any)
// Slowv logs a message at slow level.
Slowv(any)
// Sloww logs a message at slow level.
Sloww(string, ...LogField)
// WithContext returns a new logger with the given context.
WithContext(context.Context) Logger
// WithDuration returns a new logger with the given duration.
WithDuration(time.Duration) Logger
}
```

- `Error`, `Info`, `Slow`: `fmt.Sprint(…)`처럼 모든 종류의 메시지를 로그에 씁니다.
- `Errorf`, `Infof`, `Slowf`: 지정한 형식으로 메시지를 로그에 씁니다.
- `Errorv`, `Infov`, `Slowv`: 모든 종류의 메시지를 json 마샬링으로 인코딩해 로그에 씁니다.
- `Errorw`, `Infow`, `Sloww`: 지정한 `key:value` 필드와 함께 문자열 메시지를 씁니다.
- `WithContext`: 지정한 ctx를 로그 메시지에 주입합니다. 일반적으로 `trace-id`와 `span-id`를 기록하는 데 사용됩니다.
- `WithDuration`: 경과 시간을 `duration` 키로 로그 메시지에 씁니다.

## 타사 로깅 라이브러리와 통합

- zap
- 구현: [https://github.com/zeromicro/zero-contrib/blob/main/logx/zapx/zap.go](https://github.com/zeromicro/zero-contrib/blob/main/logx/zapx/zap.go)
- 사용 예시: [https://github.com/zeromicro/zero-examples/blob/main/logx/zaplog/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/zaplog/main.go)
- logrus
- 구현: [https://github.com/zeromicro/zero-contrib/blob/main/logx/logrusx/logrus.go](https://github.com/zeromicro/zero-contrib/blob/main/logx/logrusx/logrus.go)
- 사용 예시: [https://github.com/zeromicro/zero-examples/blob/main/logx/logrus/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/logrus/main.go)

더 많은 라이브러리는 직접 구현한 뒤 [https://github.com/zeromicro/zero-contrib](https://github.com/zeromicro/zero-contrib)에 PR을 보내주세요.

## 특정 저장소에 로그 쓰기

`logx`는 로그를 원하는 저장소에 쓸 수 있도록 사용자 지정할 수 있는 두 인터페이스를 정의합니다.

- `logx.NewWriter(w io.Writer)`
- `logx.SetWriter(writer logx.Writer)`

예를 들어 로그를 콘솔이나 파일 대신 kafka에 쓰고 싶다면 아래처럼 할 수 있습니다.

```go
type KafkaWriter struct {
Pusher *kq.Pusher
}

func NewKafkaWriter(pusher *kq.Pusher) *KafkaWriter {
return &KafkaWriter{
Pusher: pusher,
}
}

func (w *KafkaWriter) Write(p []byte) (n int, err error) {
// writing log with newlines, trim them.
if err := w.Pusher.Push(strings.TrimSpace(string(p))); err != nil {
return 0, err
}

return len(p), nil
}

func main() {
pusher := kq.NewPusher([]string{"localhost:9092"}, "go-zero")
defer pusher.Close()

writer := logx.NewWriter(NewKafkaWriter(pusher))
logx.SetWriter(writer)

// more code
}
```

전체 코드: [https://github.com/zeromicro/zero-examples/blob/main/logx/tokafka/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/tokafka/main.go)

## 민감한 필드 필터링

`password` 필드가 로그에 기록되지 않도록 하려면 아래처럼 할 수 있습니다.

```go
type (
Message struct {
Name string
Password string
Message string
}

SensitiveLogger struct {
logx.Writer
}
)

func NewSensitiveLogger(writer logx.Writer) *SensitiveLogger {
return &SensitiveLogger{
Writer: writer,
}
}

func (l *SensitiveLogger) Info(msg any, fields ...logx.LogField) {
if m, ok := msg.(Message); ok {
l.Writer.Info(Message{
Name: m.Name,
Password: "******",
Message: m.Message,
}, fields...)
} else {
l.Writer.Info(msg, fields...)
}
}

func main() {
// setup logx to make sure originalWriter not nil,
// the injected writer is only for filtering, like a middleware.

originalWriter := logx.Reset()
writer := NewSensitiveLogger(originalWriter)
logx.SetWriter(writer)

logx.Infov(Message{
Name: "foo",
Password: "shouldNotAppear",
Message: "bar",
})

// more code
}
```

전체 코드: [https://github.com/zeromicro/zero-examples/blob/main/logx/filterfields/main.go](https://github.com/zeromicro/zero-examples/blob/main/logx/filterfields/main.go)

## 더 많은 예제

[https://github.com/zeromicro/zero-examples/tree/main/logx](https://github.com/zeromicro/zero-examples/tree/main/logx)

## 별을 눌러주세요! ⭐

이 프로젝트가 마음에 들거나 학습 또는 자체 솔루션을 시작하는 데 사용 중이라면 star를 눌러주세요. 감사합니다!
2 changes: 1 addition & 1 deletion core/logx/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# logx

English | [简体中文](readme-cn.md)
English | [简体中文](readme-cn.md) | [한국어](readme-ko.md)

## logx configurations

Expand Down
8 changes: 4 additions & 4 deletions core/mr/readme-cn.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# mapreduce

[English](readme.md) | 简体中文
[English](readme.md) | 简体中文 | [한국어](readme-ko.md)

## 为什么需要 MapReduce

在实际的业务场景中我们常常需要从不同的 rpc 服务中获取相应属性来组装成复杂对象。
在实际的业务场景中我们常常需要从不同的 RPC 服务中获取相应属性来组装成复杂对象。

比如要查询商品详情:

Expand All @@ -13,9 +13,9 @@
3. 价格服务-查询价格属性
4. 营销服务-查询营销属性

如果是串行调用的话响应时间会随着 rpc 调用次数呈线性增长,所以我们要优化性能一般会将串行改并行。
如果是串行调用的话响应时间会随着 RPC 调用次数呈线性增长,所以我们要优化性能一般会将串行改并行。

简单的场景下使用 `WaitGroup` 也能够满足需求,但是如果我们需要对 rpc 调用返回的数据进行校验、数据加工转换、数据汇总呢?继续使用 `WaitGroup` 就有点力不从心了,go 的官方库中并没有这种工具(java 中提供了 CompleteFuture),我们依据 MapReduce 架构思想实现了进程内的数据批处理 MapReduce 并发工具类。
简单的场景下使用 `WaitGroup` 也能够满足需求,但是如果我们需要对 RPC 调用返回的数据进行校验、数据加工转换、数据汇总呢?继续使用 `WaitGroup` 就有点力不从心了,go 的官方库中并没有这种工具(java 中提供了 CompletableFuture),我们依据 MapReduce 架构思想实现了进程内的数据批处理 MapReduce 并发工具类。

## 设计思路

Expand Down
89 changes: 89 additions & 0 deletions core/mr/readme-ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<img align="right" width="150px" src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png">

# mapreduce

[English](readme.md) | [简体中文](readme-cn.md) | 한국어

## MapReduce가 필요한 이유

실제 비즈니스 시나리오에서는 서로 다른 RPC 서비스에서 속성을 가져와 복잡한 객체를 조립해야 하는 경우가 많습니다.

예를 들어 상품 상세 정보를 조회한다고 해봅시다.

1. 상품 서비스 - 상품 속성 조회
2. 재고 서비스 - 재고 속성 조회
3. 가격 서비스 - 가격 속성 조회
4. 마케팅 서비스 - 마케팅 속성 조회

직렬 호출이라면 RPC 호출 횟수에 따라 응답 시간이 선형적으로 증가하므로, 일반적으로 응답 시간을 최적화하기 위해 직렬 호출을 병렬 호출로 바꿉니다.

단순한 시나리오에서는 `WaitGroup`만으로도 요구 사항을 충족할 수 있습니다. 하지만 RPC 호출이 반환한 데이터를 검증하거나, 데이터를 처리하거나, 데이터를 집계해야 한다면 어떻게 해야 할까요? Go 표준 라이브러리에는 이런 도구가 없습니다(Java에는 CompletableFuture가 제공됩니다). 그래서 우리는 MapReduce 아키텍처를 기반으로 프로세스 내부 데이터 배치 처리를 위한 MapReduce 동시성 도구를 구현했습니다.

## 설계 아이디어

동시성 도구가 필요한 비즈니스 시나리오를 작성자의 관점에서 정리해봅시다.

1. 상품 상세 조회: 여러 서비스를 동시에 호출해 상품 속성을 조합하고, 호출 오류가 발생하면 즉시 종료할 수 있어야 합니다.
2. 상품 상세 페이지에서 사용자 쿠폰 자동 추천: 쿠폰을 동시에 검증하고, 검증에 실패한 쿠폰은 자동으로 제외하며, 나머지를 모두 반환할 수 있어야 합니다.

위 시나리오는 모두 입력 데이터를 처리한 뒤 정제된 데이터를 출력하는 과정입니다. 데이터 처리에는 아주 고전적인 비동기 패턴인 생산자-소비자 패턴이 있습니다. 따라서 데이터 배치 처리의 생명 주기를 추상화하면 대략 세 단계로 나눌 수 있습니다.

<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/mapreduce-serial-en.png" width="500">

1. 데이터 생성(generator)
2. 데이터 처리(mapper)
3. 데이터 집계(reducer)

데이터 생성은 필수 단계이고, 데이터 처리와 데이터 집계는 선택 단계입니다. 데이터 생성과 처리는 동시 호출을 지원하며, 데이터 집계는 기본적으로 순수 메모리 작업이므로 단일 고루틴으로 처리할 수 있습니다.

서로 다른 데이터 처리 단계가 서로 다른 고루틴에서 수행되므로, 고루틴 간 통신을 위해 채널을 사용하는 것이 자연스럽습니다.

<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/mapreduce-en.png" width="500">

언제든 프로세스를 종료하려면 어떻게 해야 할까요?

간단합니다. 고루틴에서 채널 또는 전달된 context의 완료 신호를 감시하면 됩니다.

## 간단한 예시

동시성을 시뮬레이션하며 제곱합을 계산합니다.

```go
package main

import (
"fmt"
"log"

"github.com/zeromicro/go-zero/core/mr"
)

func main() {
val, err := mr.MapReduce(func(source chan<- int) {
// generator
for i := 0; i < 10; i++ {
source <- i
}
}, func(i int, writer mr.Writer[int], cancel func(error)) {
// mapper
writer.Write(i * i)
}, func(pipe <-chan int, writer mr.Writer[int], cancel func(error)) {
// reducer
var sum int
for i := range pipe {
sum += i
}
writer.Write(sum)
})
if err != nil {
log.Fatal(err)
}
fmt.Println("result:", val)
}
```

더 많은 예제: [https://github.com/zeromicro/zero-examples/tree/main/mapreduce](https://github.com/zeromicro/zero-examples/tree/main/mapreduce)

## 별을 눌러주세요! ⭐

이 프로젝트가 마음에 들거나 학습 또는 자체 솔루션을 시작하는 데 사용 중이라면 star를 눌러주세요. 감사합니다!
8 changes: 4 additions & 4 deletions core/mr/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

# mapreduce

English | [简体中文](readme-cn.md)
English | [简体中文](readme-cn.md) | [한국어](readme-ko.md)

## Why MapReduce is needed

In practical business scenarios we often need to get the corresponding properties from different rpc services to assemble complex objects.
In practical business scenarios we often need to get the corresponding properties from different RPC services to assemble complex objects.

For example, to query product details.

Expand All @@ -15,9 +15,9 @@ For example, to query product details.
3. price service - query price attributes
4. marketing service - query marketing properties

If it is a serial call, the response time will increase linearly with the number of rpc calls, so we will generally change serial to parallel to optimize response time.
If it is a serial call, the response time will increase linearly with the number of RPC calls, so we will generally change serial to parallel to optimize response time.

Simple scenarios using `WaitGroup` can also meet the needs, but what if we need to check the data returned by the rpc call, data processing, data aggregation? The official go library does not have such a tool (CompleteFuture is provided in java), so we implemented an in-process data batching MapReduce concurrent tool based on the MapReduce architecture.
Simple scenarios using `WaitGroup` can also meet the needs, but what if we need to check the data returned by the RPC call, data processing, data aggregation? The official go library does not have such a tool (CompletableFuture is provided in java), so we implemented an in-process data batching MapReduce concurrent tool based on the MapReduce architecture.

## Design ideas

Expand Down
2 changes: 1 addition & 1 deletion readme-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

***缩短从需求到上线的距离***

[English](readme.md) | 简体中文
[English](readme.md) | 简体中文 | [한국어](readme-ko.md)

[![Go Report Card](https://goreportcard.com/badge/github.com/zeromicro/go-zero)](https://goreportcard.com/report/github.com/zeromicro/go-zero)
[![goproxy](https://goproxy.cn/stats/github.com/zeromicro/go-zero/badges/download-count.svg)](https://goproxy.cn/stats/github.com/zeromicro/go-zero/badges/download-count.svg)
Expand Down
Loading