Compare commits

...

121 Commits

Author SHA1 Message Date
654d8fa7e4 Merge pull request 'tracer: add Flush method' (#224) from tracerimp into master
Reviewed-on: #224
2023-07-04 00:24:09 +03:00
dd1a9cd25a tracer: add Flush method
Some checks failed
lint / lint (pull_request) Failing after 1m29s
pr / test (pull_request) Failing after 2m37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-07-04 00:23:50 +03:00
d463eb20cb Merge pull request 'util/time: ParseDuration fix' (#221) from timefix into master
Reviewed-on: #221
2023-05-29 13:59:59 +03:00
8d5e25f8cf util/time: ParseDuration fix
Some checks failed
lint / lint (pull_request) Successful in 1m8s
pr / test (pull_request) Failing after 1m6s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-29 13:59:30 +03:00
27e8043fed Merge pull request 'util/time: fix duration parsing' (#220) from mastertime into master
Reviewed-on: #220
2023-05-27 23:57:27 +03:00
4e86df1721 util/time: fix duration parsing
Some checks failed
pr / test (pull_request) Failing after 1m6s
lint / lint (pull_request) Successful in 1m10s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-27 23:56:51 +03:00
03de1ec38f Merge pull request 'cleanup interfaces for v4' (#217) from v4 into master
Reviewed-on: #217
2023-05-09 20:04:40 +03:00
819ad1117a cleanup interfaces for v4
Some checks failed
lint / lint (pull_request) Successful in 1m9s
pr / test (pull_request) Failing after 1m4s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-09 20:04:15 +03:00
a2a383606d Merge pull request 'util/test: update test cases code' (#216) from testcase into master
Reviewed-on: #216
2023-04-28 07:10:40 +03:00
55ce58617b util/test: update test cases code
Some checks failed
lint / lint (pull_request) Successful in 51s
pr / test (pull_request) Failing after 52s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-28 07:10:19 +03:00
0e587d923e Merge pull request 'meter: move metrics handling in broker implementations' (#215) from metrics into master
Reviewed-on: #215
2023-04-27 15:32:56 +03:00
fa0248c80c cleanup
All checks were successful
pr / test (pull_request) Successful in 50s
lint / lint (pull_request) Successful in 49s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-27 15:31:59 +03:00
054bd02b59 meter: move metrics handling in broker implementations
All checks were successful
lint / lint (pull_request) Successful in 1m4s
pr / test (pull_request) Successful in 50s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-27 15:30:55 +03:00
0cf246d2d6 Merge pull request 'util/io: add RedirectStderr' (#214) from io-redirect into master
Reviewed-on: #214
2023-04-24 12:59:31 +03:00
af278bd7d3 util/io: add RedirectStderr
All checks were successful
lint / lint (pull_request) Successful in 46s
pr / test (pull_request) Successful in 50s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-24 12:58:05 +03:00
814b90efe5 Merge pull request 'util/test: export GetCases func' (#213) from GetCases into master
Reviewed-on: #213
2023-04-19 01:23:53 +03:00
e403ae3d8e util/test: export GetCases func
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-19 01:23:34 +03:00
c9816a3957 Merge pull request 'util/test: add helper funcs' (#212) from test into master
Reviewed-on: #212
2023-04-19 00:33:28 +03:00
5691238a6a util/test: add helper funcs
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-18 23:47:12 +03:00
963a0fa7b7 Merge pull request 'gofmt -s code' (#209) from gofmt into master
Reviewed-on: #209
2023-04-11 23:34:41 +03:00
485257035c gofmt -s code
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-11 23:32:58 +03:00
ebd8ddf05b move to v4
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-11 22:21:25 +03:00
1d5e795443 Merge pull request 'move RawMessage to codec package' (#208) from rawmsg into v3
Reviewed-on: #208
2023-04-02 14:15:02 +03:00
a3a434d923 move RawMessage to codec package
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-02 14:10:57 +03:00
bcc06054f1 Merge pull request 'updates' (#207) from updates into v3
Reviewed-on: #207
2023-03-24 00:29:33 +03:00
270d26f1ae test
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-24 00:25:47 +03:00
646212cc08 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 17:09:40 +03:00
00c2c749db retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:46:41 +03:00
2dbada0e94 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:43:33 +03:00
7b8f4410fb retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:36:38 +03:00
45ebef5544 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:31:42 +03:00
cf4cac0733 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:29:58 +03:00
50d60b5825 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:24:47 +03:00
46ef491764 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:17:44 +03:00
a51b8b8102 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:11:52 +03:00
15aac48f1e retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:08:51 +03:00
078069b2d7 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:01:27 +03:00
258812304a retest
Some checks failed
autoapprove
automerge
test
lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 15:12:32 +03:00
da5d50db5b retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:48:27 +03:00
384e4d113d retest
All checks were successful
test
lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:43:23 +03:00
dfd1da7f0d retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:26:03 +03:00
8e5015e580 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:41:59 +03:00
bd0c309b71 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:38:39 +03:00
b4f0c3e29a retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:29:21 +03:00
8fddaa0455 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:26:42 +03:00
2710c269a8 actions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:21:59 +03:00
70ea93e466 actions
Some checks failed
automerge
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:12:10 +03:00
a87d0ab1c1 update deps
Some checks failed
autoapprove
automerge
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 00:43:15 +03:00
2e5e102719 Merge pull request 'config: another fix for Default funcs' (#193) from defaults-fix into v3
Reviewed-on: #193
2023-03-16 07:14:41 +03:00
36e492314d config: another fix for Default funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-16 07:14:21 +03:00
0c78873277 Merge pull request 'config: fix Default funcs' (#192) from config-fix into v3
Reviewed-on: #192
2023-03-15 22:53:21 +03:00
7f57dc09d3 config: fix Default funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-15 22:51:40 +03:00
447206d256 Merge pull request 'config: proper handle AllowFail option' (#191) from config-fix into v3
Reviewed-on: #191
2023-03-14 13:24:37 +03:00
33a7feb970 config: proper handle AllowFail option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-14 13:23:41 +03:00
3950f2ed86 Merge pull request 'config: add Before/After Init funcs' (#190) from config-improve into v3
Reviewed-on: #190
2023-03-14 10:40:12 +03:00
68c1048a7d config: add Before/After Init funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-14 10:38:24 +03:00
bff40bd317 Merge pull request #189 from unistack-org/meter-handler
meter/handler: move to go.unistack.org/micro-meter-handler/v3
2023-02-26 13:48:09 +03:00
2878d0a4ea server/health: move to go.unistack.org/micro-server-health/v3
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-26 13:46:10 +03:00
3138a9fded meter/handler: move to go.unistack.org/micro-meter-handler/v3
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-26 13:42:04 +03:00
742b99636a fix and regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-22 00:09:52 +03:00
34387f129d Merge pull request #188 from unistack-org/proto
fix service names in proto
2023-02-21 13:28:13 +03:00
47075acb06 fix service names in proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 13:26:01 +03:00
09cb998ba4 fix service names in proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 13:18:03 +03:00
b9dbfb1cfc Merge pull request #187 from unistack-org/api-REMOVE
api: remove and regen
2023-02-21 02:12:47 +03:00
56efccc4cf api: remove and regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 02:10:24 +03:00
ce9f896287 Merge pull request #186 from unistack-org/structfs
util/structfs: import https://github.com/unistack-org/go-structfs
2023-02-19 23:38:31 +03:00
83d87a40e4 util/structfs: import https://github.com/unistack-org/go-structfs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-19 23:35:39 +03:00
75fd1e43b9 Merge pull request #185 from unistack-org/server
server: add server.SetHandlerOption helper
2023-02-13 23:33:57 +03:00
395a3eed3d server: add server.SetHandlerOption helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 23:31:38 +03:00
3ba8cb7f9e Merge pull request #184 from unistack-org/duration
util/time: add Marshal/Unmarshal to own Duration
2023-02-13 14:05:16 +03:00
b07806b9a1 tmp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 14:03:02 +03:00
0f583218d4 tmp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 14:02:08 +03:00
f4d0237785 Merge pull request #183 from unistack-org/logger/unwrap
logger/unwrap: fix for nested tagged/untagged
2023-02-08 14:56:51 +03:00
0f343dad0b logger/unwrap: fix for nested tagged/untagged
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-08 14:54:37 +03:00
7c29afba0b Merge pull request #182 from unistack-org/timeDuration
config/default: handle time.Duration
2023-02-07 06:50:42 +03:00
8159b9d233 config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:48:36 +03:00
45cdac5c29 config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:48:12 +03:00
98db0dc8bc config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:47:46 +03:00
453d2232bd Merge pull request #181 from unistack-org/unwrap
logger/unwrap: check nested in case of Tagged
2023-02-06 22:38:42 +03:00
9b387312da logger/unwrap: check nested in case of Tagged
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 22:36:24 +03:00
84024f7713 Merge pull request #180 from unistack-org/logger-wrapper
logger/unwrap: support sql and proto wrapper types
2023-02-06 18:55:39 +03:00
5a554f9f0c fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 18:53:27 +03:00
9c33cbc8e2 logger/unwrap: support sql and proto wrapper types
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 18:50:00 +03:00
848fe1c0d4 Merge pull request #179 from unistack-org/logger-unwrap
logger/unwrap: fix unwrap format
2023-02-03 23:36:47 +03:00
6cbf23fec5 logger/unwrap: fix unwrap format
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-03 23:33:24 +03:00
7462b0b53c Merge pull request #178 from unistack-org/fsm
fsm: improve and convert to interface
2023-01-30 00:31:50 +03:00
cb743cee3f logger: remove wrappers support and OmitLogger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-30 00:29:49 +03:00
d18952951c fsm: improve and convert to interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-30 00:17:29 +03:00
dependabot[bot]
f6b7f1b4bc chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 (#172)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.5...v1.3.6)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:07:13 +03:00
dependabot[bot]
33fa702104 chore(deps): bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#173)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 09:54:07 +03:00
4debc392d1 Merge pull request #171 from unistack-org/fixspan
add span status method
2023-01-18 09:51:39 +03:00
7137d99102 add span status method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 09:48:58 +03:00
f76b3171d9 Merge pull request #170 from unistack-org/tracer
tracer: add span kind
2023-01-18 00:28:02 +03:00
db3fc42204 tracer: add span kind
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 00:25:15 +03:00
f59023d741 tracer: add span kind
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 00:21:18 +03:00
6f17fd891a Merge pull request #169 from unistack-org/tracer
tracer/wrapper: fix observers
2023-01-17 23:39:49 +03:00
fd93308e8e tracer/wrapper: fix observers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 23:37:25 +03:00
2aaa0a74d8 tracer/wrapper: fix observers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 23:31:27 +03:00
ccbf23688b Merge pull request #168 from unistack-org/tracer
tracer: add context to Options
2023-01-17 08:33:35 +03:00
3bd6db79cb tracer: add context to Options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 08:30:57 +03:00
9347bb0651 use no default content-type (#165)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-07 23:38:53 +03:00
0d63723ed3 Merge pull request #164 from unistack-org/logger-clone-fix
logger: fix Clone
2023-01-06 22:44:26 +03:00
a7f84e0baa logger: fix Clone
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-06 22:41:57 +03:00
c209892ce8 Merge pull request #163 from unistack-org/logger_unwrap
logger/unwrap: fix Tagged option
2022-12-29 23:19:57 +03:00
421842315f logger/unwrap: fix Tagged option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-29 23:16:58 +03:00
25350a6531 Merge pull request #162 from unistack-org/util_http_method_not_allowed
util/http: trie support method not allowed
2022-12-27 23:49:36 +03:00
5e47cc7e8c util/http: trie support method not allowed
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-27 23:47:11 +03:00
1687b98b11 Merge pull request #161 from unistack-org/tracer
tracer: add labels method
2022-12-24 19:22:48 +03:00
a81649d2a2 tracer: add labels method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 19:20:22 +03:00
b48faa3b2b Merge pull request #160 from unistack-org/tracer
tracer: fix span options
2022-12-24 18:20:59 +03:00
0be584ef0d fix wrapper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 18:18:42 +03:00
26a2d18766 tracer: fix span options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 18:09:48 +03:00
25a796fe4f Merge pull request #158 from unistack-org/meter
meter/wrapper: fix naming
2022-11-28 14:39:45 +03:00
d23de14769 meter/wrapper: fix naming
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-28 14:36:54 +03:00
2fb108519c Merge pull request #157 from unistack-org/fixups
fix wrappers
2022-11-27 00:45:44 +03:00
c7ce238da3 fix wrappers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-27 00:43:30 +03:00
dependabot[bot]
67aa79f18a chore(deps): bump hmarr/auto-approve-action from 2 to 3 (#155)
Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 2 to 3.
- [Release notes](https://github.com/hmarr/auto-approve-action/releases)
- [Commits](https://github.com/hmarr/auto-approve-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: hmarr/auto-approve-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-19 21:45:49 +03:00
e6c3d734a3 Merge pull request #156 from unistack-org/take_unwrap
logger/unwrap: add tagged option
2022-11-19 15:23:22 +03:00
1374e27531 logger/unwrap: add tagged option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-19 15:20:42 +03:00
1060f6a4c3 Merge pull request #154 from unistack-org/client_metadata
client: fix WithResponseMetadata CallOption
2022-11-14 16:13:22 +03:00
7d72ab05c6 client: fix WithResponseMetadata CallOption
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-14 16:10:54 +03:00
187 changed files with 3364 additions and 3624 deletions

View File

@@ -0,0 +1,18 @@
---
name: Bug report
about: For reporting bugs in micro
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
1. What are you trying to do?
2. What did you expect to happen?
3. What happens instead?
**How to reproduce the bug:**
If possible, please include a minimal code snippet here.

View File

@@ -0,0 +1,17 @@
---
name: Feature request / Enhancement
about: If you have a need not served by micro
title: "[FEATURE]"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,8 @@
---
name: Question
about: Ask a question about micro
title: ''
labels: ''
assignees: ''
---

28
.gitea/autoapprove.yml Normal file
View File

@@ -0,0 +1,28 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
workflow_run:
workflows: ["prbuild"]
types:
- completed
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
run: [ "curl -o tea https://dl.gitea.com/tea/main/tea-main-linux-amd64",
"chmod +x ./tea",
"./tea login add --name unistack --token ${{ secrets.GITHUB_TOKEN }} --url https://git.unistack.org",
"./tea pr --repo ${{ github.event.repository.name }}"
]
if: github.actor == 'vtolstov'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

24
.gitea/workflows/lint.yml Normal file
View File

@@ -0,0 +1,24 @@
name: lint
on:
pull_request:
branches:
- master
- v3
jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: setup-go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: 1.18
- name: checkout
uses: https://gitea.com/actions/checkout@v3
- name: deps
run: go get -v -d ./...
- name: lint
uses: https://github.com/golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
version: v1.52

23
.gitea/workflows/pr.yml Normal file
View File

@@ -0,0 +1,23 @@
name: pr
on:
pull_request:
branches:
- master
- v3
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: https://gitea.com/actions/checkout@v3
- name: setup-go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: 1.18
- name: deps
run: go get -v -t -d ./...
- name: test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...

View File

@@ -0,0 +1,9 @@
## Pull Request template
Please, go through these steps before clicking submit on this PR.
1. Give a descriptive title to your PR.
2. Provide a description of your changes.
3. Make sure you have some relevant tests.
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: approve
uses: hmarr/auto-approve-action@v2
uses: hmarr/auto-approve-action@v3
if: github.actor == 'vtolstov' || github.actor == 'dependabot[bot]'
id: approve
with:

View File

@@ -34,7 +34,7 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- name: lint
uses: golangci/golangci-lint-action@v3.3.1
uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- name: metadata
id: metadata
uses: dependabot/fetch-metadata@v1.3.5
uses: dependabot/fetch-metadata@v1.3.6
with:
github-token: "${{ secrets.TOKEN }}"
- name: merge

View File

@@ -34,14 +34,7 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- name: lint
uses: golangci/golangci-lint-action@v3.3.1
uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
version: v1.30

View File

@@ -1,4 +1,4 @@
# Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/unistack-org/micro/v3?tab=overview) [![Status](https://github.com/unistack-org/micro/workflows/build/badge.svg?branch=master)](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Lint](https://goreportcard.com/badge/go.unistack.org/micro/v3)](https://goreportcard.com/report/go.unistack.org/micro/v3) [![Coverage](https://codecov.io/gh/unistack-org/micro/branch/v3/graph/badge.svg?token=OZPO2LP7VS)](https://codecov.io/gh/unistack-org/micro)
# Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/go.unistack.org/micro/v4?tab=overview) [![Status](https://github.com/unistack-org/micro/workflows/build/badge.svg?branch=master)](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Lint](https://goreportcard.com/badge/go.unistack.org/micro/v4)](https://goreportcard.com/report/go.unistack.org/micro/v4) [![Coverage](https://codecov.io/gh/unistack-org/micro/branch/v4/graph/badge.svg?token=OZPO2LP7VS)](https://codecov.io/gh/unistack-org/micro)
Micro is a standard library for microservices.

View File

@@ -1,182 +0,0 @@
package api // import "go.unistack.org/micro/v3/api"
import (
"errors"
"regexp"
"strings"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/register"
"go.unistack.org/micro/v3/server"
)
// nolint: revive
// Api interface
type Api interface {
// Initialise options
Init(...Option) error
// Get the options
Options() Options
// Register a http handler
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Implementation of api
String() string
}
// Options holds the options
type Options struct{}
// Option func signature
type Option func(*Options) error
// Endpoint is a mapping between an RPC method and HTTP endpoint
type Endpoint struct {
// Name Greeter.Hello
Name string
// Desciption for endpoint
Description string
// Handler e.g rpc, proxy
Handler string
// Body destination
// "*" or "" - top level message value
// "string" - inner message value
Body string
// Host e.g example.com
Host []string
// Method e.g GET, POST
Method []string
// Path e.g /greeter. Expect POSIX regex
Path []string
// Stream flag
Stream bool
}
// Service represents an API service
type Service struct {
// Name of service
Name string
// Endpoint for this service
Endpoint *Endpoint
// Services that provides service
Services []*register.Service
}
// Encode encodes an endpoint to endpoint metadata
func Encode(e *Endpoint) map[string]string {
if e == nil {
return nil
}
// endpoint map
ep := make(map[string]string)
// set vals only if they exist
set := func(k, v string) {
if len(v) == 0 {
return
}
ep[k] = v
}
set("endpoint", e.Name)
set("description", e.Description)
set("handler", e.Handler)
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
set("body", e.Body)
return ep
}
// Decode decodes endpoint metadata into an endpoint
func Decode(e metadata.Metadata) *Endpoint {
if e == nil {
return nil
}
ep := &Endpoint{}
ep.Name, _ = e.Get("endpoint")
ep.Description, _ = e.Get("description")
epmethod, _ := e.Get("method")
ep.Method = []string{epmethod}
eppath, _ := e.Get("path")
ep.Path = []string{eppath}
ephost, _ := e.Get("host")
ep.Host = []string{ephost}
ep.Handler, _ = e.Get("handler")
ep.Body, _ = e.Get("body")
return ep
}
// Validate validates an endpoint to guarantee it won't blow up when being served
func Validate(e *Endpoint) error {
if e == nil {
return errors.New("endpoint is nil")
}
if len(e.Name) == 0 {
return errors.New("name required")
}
for _, p := range e.Path {
ps := p[0]
pe := p[len(p)-1]
switch {
case ps == '^' && pe == '$':
if _, err := regexp.CompilePOSIX(p); err != nil {
return err
}
case ps == '^' && pe != '$':
return errors.New("invalid path")
case ps != '^' && pe == '$':
return errors.New("invalid path")
}
}
if len(e.Handler) == 0 {
return errors.New("invalid handler")
}
return nil
}
/*
Design ideas
// Gateway is an api gateway interface
type Gateway interface {
// Register a http handler
Handle(pattern string, http.Handler)
// Register a route
RegisterRoute(r Route)
// Init initialises the command line.
// It also parses further options.
Init(...Option) error
// Run the gateway
Run() error
}
// NewGateway returns a new api gateway
func NewGateway() Gateway {
return newGateway()
}
*/
// WithEndpoint returns a server.HandlerOption with endpoint metadata set
//
// Usage:
//
// proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
// &api.Endpoint{
// Name: "Greeter.Hello",
// Path: []string{"/greeter"},
// },
// ))
func WithEndpoint(e *Endpoint) server.HandlerOption {
return server.EndpointMetadata(e.Name, Encode(e))
}

View File

@@ -1,245 +0,0 @@
package api
import (
"strings"
"testing"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/server"
)
func TestDecode(t *testing.T) {
md := metadata.New(0)
md.Set("host", "localhost", "method", "GET", "path", "/")
ep := Decode(md)
if md == nil {
t.Fatalf("failed to decode md %#+v", md)
} else if len(ep.Host) != 1 || len(ep.Method) != 1 || len(ep.Path) != 1 {
t.Fatalf("ep invalid after decode %#+v", ep)
}
if ep.Host[0] != "localhost" || ep.Method[0] != "GET" || ep.Path[0] != "/" {
t.Fatalf("ep invalid after decode %#+v", ep)
}
}
//nolint:gocyclo
func TestEncode(t *testing.T) {
testData := []*Endpoint{
nil,
{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test"},
},
}
compare := func(expect, got []string) bool {
// no data to compare, return true
if len(expect) == 0 && len(got) == 0 {
return true
}
// no data expected but got some return false
if len(expect) == 0 && len(got) > 0 {
return false
}
// compare expected with what we got
for _, e := range expect {
var seen bool
for _, g := range got {
if e == g {
seen = true
break
}
}
if !seen {
return false
}
}
// we're done, return true
return true
}
for _, d := range testData {
// encode
e := Encode(d)
// decode
de := Decode(e)
// nil endpoint returns nil
if d == nil {
if e != nil {
t.Fatalf("expected nil got %v", e)
}
if de != nil {
t.Fatalf("expected nil got %v", de)
}
continue
}
// check encoded map
name := e["endpoint"]
desc := e["description"]
method := strings.Split(e["method"], ",")
path := strings.Split(e["path"], ",")
host := strings.Split(e["host"], ",")
handler := e["handler"]
if name != d.Name {
t.Fatalf("expected %v got %v", d.Name, name)
}
if desc != d.Description {
t.Fatalf("expected %v got %v", d.Description, desc)
}
if handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, handler)
}
if ok := compare(d.Method, method); !ok {
t.Fatalf("expected %v got %v", d.Method, method)
}
if ok := compare(d.Path, path); !ok {
t.Fatalf("expected %v got %v", d.Path, path)
}
if ok := compare(d.Host, host); !ok {
t.Fatalf("expected %v got %v", d.Host, host)
}
if de.Name != d.Name {
t.Fatalf("expected %v got %v", d.Name, de.Name)
}
if de.Description != d.Description {
t.Fatalf("expected %v got %v", d.Description, de.Description)
}
if de.Handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, de.Handler)
}
if ok := compare(d.Method, de.Method); !ok {
t.Fatalf("expected %v got %v", d.Method, de.Method)
}
if ok := compare(d.Path, de.Path); !ok {
t.Fatalf("expected %v got %v", d.Path, de.Path)
}
if ok := compare(d.Host, de.Host); !ok {
t.Fatalf("expected %v got %v", d.Host, de.Host)
}
}
}
func TestValidate(t *testing.T) {
epPcre := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"^/test/?$"},
}
if err := Validate(epPcre); err != nil {
t.Fatal(err)
}
epGpath := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
if err := Validate(epGpath); err != nil {
t.Fatal(err)
}
epPcreInvalid := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/?$"},
}
if err := Validate(epPcreInvalid); err == nil {
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
}
}
func TestWithEndpoint(t *testing.T) {
ep := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
o := WithEndpoint(ep)
opts := server.NewHandlerOptions(o)
if opts.Metadata == nil {
t.Fatalf("WithEndpoint not works %#+v", opts)
}
md, ok := opts.Metadata[ep.Name]
if !ok {
t.Fatalf("WithEndpoint not works %#+v", opts)
}
if v, ok := md.Get("Endpoint"); !ok || v != "Foo.Bar" {
t.Fatalf("WithEndpoint not works %#+v", md)
}
}
func TestValidateNilErr(t *testing.T) {
var ep *Endpoint
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateMissingNameErr(t *testing.T) {
ep := &Endpoint{}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateMissingHandlerErr(t *testing.T) {
ep := &Endpoint{Name: "test"}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpStartErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test"}
ep.Path = []string{"^/"}
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpEndErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "/$"
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpNonErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "^/(.*)$"
if err := Validate(ep); err != nil {
t.Fatalf("Validate not works")
}
}
func TestValidateRegexpErr(t *testing.T) {
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
ep.Path[0] = "^/(.$"
if err := Validate(ep); err == nil {
t.Fatalf("Validate not works")
}
}

View File

@@ -1,21 +1,24 @@
// Package broker is an interface used for asynchronous messaging
package broker // import "go.unistack.org/micro/v3/broker"
package broker // import "go.unistack.org/micro/v4/broker"
import (
"context"
"errors"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/metadata"
)
// DefaultBroker default memory broker
var DefaultBroker = NewBroker()
var DefaultBroker Broker // = NewBroker()
var (
// ErrNotConnected returns when broker used but not connected yet
ErrNotConnected = errors.New("broker not connected")
// ErrDisconnected returns when broker disconnected
ErrDisconnected = errors.New("broker disconnected")
// ErrInvalidMessage returns when message has nvalid format
ErrInvalidMessage = errors.New("broker message has invalid format")
)
// Broker is an interface used for asynchronous messaging.
@@ -32,91 +35,42 @@ type Broker interface {
Connect(ctx context.Context) error
// Disconnect disconnect from broker
Disconnect(ctx context.Context) error
// NewMessage creates new broker message
NewMessage(endpoint string, req interface{}, opts ...MessageOption) Message
// Publish message to broker topic
Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error
Publish(ctx context.Context, msg interface{}, opts ...PublishOption) error
// Subscribe subscribes to topic message via handler
Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
// BatchPublish messages to broker with multiple topics
BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error
// BatchSubscribe subscribes to topic messages via handler
BatchSubscribe(ctx context.Context, topic string, h BatchHandler, opts ...SubscribeOption) (Subscriber, error)
Subscribe(ctx context.Context, topic string, handler interface{}, opts ...SubscribeOption) (Subscriber, error)
// String type of broker
String() string
}
// Handler is used to process messages via a subscription of a topic.
type Handler func(Event) error
// Events contains multiple events
type Events []Event
// Ack try to ack all events and return
func (evs Events) Ack() error {
var err error
for _, ev := range evs {
if err = ev.Ack(); err != nil {
return err
}
}
return nil
}
// SetError sets error on event
func (evs Events) SetError(err error) {
for _, ev := range evs {
ev.SetError(err)
}
}
// BatchHandler is used to process messages in batches via a subscription of a topic.
type BatchHandler func(Events) error
// Event is given to a subscription handler for processing
type Event interface {
// Message is given to a subscription handler for processing
type Message interface {
// Context for the message
Context() context.Context
// Topic returns event topic
Topic() string
// Message returns broker message
Message() *Message
// Body returns broker message
Body() interface{}
// Ack acknowledge message
Ack() error
// Error returns message error (like decoding errors or some other)
// In this case Body contains raw []byte from broker
Error() error
// SetError set event processing error
SetError(err error)
}
// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return *m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawMessage UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}
// Message is used to transfer data
type Message struct {
// RawMessage is used to transfer data
type RawMessage struct {
// Header contains message metadata
Header metadata.Metadata
// Body contains message body
Body RawMessage
Body codec.RawMessage
}
// NewMessage create broker message with topic filled
func NewMessage(topic string) *Message {
m := &Message{Header: metadata.New(2)}
func NewRawMessage(topic string) *RawMessage {
m := &RawMessage{Header: metadata.New(2)}
m.Header.Set(metadata.HeaderTopic, topic)
return m
}

View File

@@ -1,15 +1,17 @@
//go:build ignore
package broker
import (
"context"
"sync"
"time"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
maddr "go.unistack.org/micro/v3/util/addr"
"go.unistack.org/micro/v3/util/id"
mnet "go.unistack.org/micro/v3/util/net"
"go.unistack.org/micro/v3/util/rand"
"go.unistack.org/micro/v4/logger"
maddr "go.unistack.org/micro/v4/util/addr"
"go.unistack.org/micro/v4/util/id"
mnet "go.unistack.org/micro/v4/util/net"
"go.unistack.org/micro/v4/util/rand"
)
type memoryBroker struct {
@@ -20,23 +22,6 @@ type memoryBroker struct {
connected bool
}
type memoryEvent struct {
err error
message interface{}
topic string
opts Options
}
type memorySubscriber struct {
ctx context.Context
exit chan bool
handler Handler
batchhandler BatchHandler
id string
topic string
opts SubscribeOptions
}
func (m *memoryBroker) Options() Options {
return m.opts
}
@@ -88,16 +73,11 @@ func (m *memoryBroker) Init(opts ...Option) error {
return nil
}
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
msg.Header.Set(metadata.HeaderTopic, topic)
return m.publish(ctx, []*Message{msg}, opts...)
func (m *memoryBroker) NewMessage(endpoint string, req interface{}, opts ...MessageOption) Message {
return &memoryMessage{}
}
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
return m.publish(ctx, msgs, opts...)
}
func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
func (m *memoryBroker) Publish(ctx context.Context, message interface{}, opts ...PublishOption) error {
m.RLock()
if !m.connected {
m.RUnlock()
@@ -112,128 +92,94 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...Pub
return ctx.Err()
default:
options := NewPublishOptions(opts...)
msgTopicMap := make(map[string]Events)
for _, v := range msgs {
p := &memoryEvent{opts: m.opts}
if m.opts.Codec == nil || options.BodyOnly {
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
p.message = v.Body
} else {
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
p.message, err = m.opts.Codec.Marshal(v)
if err != nil {
return err
var msgs []*memoryMessage
switch v := message.(type) {
case *memoryMessage:
msgs = []*memoryMessage{v}
case []*memoryMessage:
msgs = v
default:
return ErrInvalidMessage
}
msgTopicMap := make(map[string][]*memoryMessage)
for _, msg := range msgs {
p := &memoryMessage{opts: options}
/*
if mb, ok := msg.Body().(*codec.Frame); ok {
p.message = v.Body
} else {
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
p.message, err = m.opts.Codec.Marshal(v)
if err != nil {
return err
}
}
}
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
*/
msgTopicMap[msg.Topic()] = append(msgTopicMap[p.topic], p)
}
beh := m.opts.BatchErrorHandler
eh := m.opts.ErrorHandler
for t, ms := range msgTopicMap {
ts := time.Now()
m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(len(ms))
m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(len(ms))
m.RLock()
subs, ok := m.subscribers[t]
m.RUnlock()
if !ok {
m.opts.Meter.Counter(PublishMessageTotal, "endpoint", t, "status", "failure").Add(len(ms))
m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(-len(ms))
m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(-len(ms))
continue
}
m.opts.Meter.Counter(PublishMessageTotal, "endpoint", t, "status", "success").Add(len(ms))
for _, sub := range subs {
if sub.opts.BatchErrorHandler != nil {
beh = sub.opts.BatchErrorHandler
}
if sub.opts.ErrorHandler != nil {
eh = sub.opts.ErrorHandler
}
switch {
// batch processing
case sub.batchhandler != nil:
if err = sub.batchhandler(ms); err != nil {
ms.SetError(err)
if beh != nil {
_ = beh(ms)
for _, p := range ms {
if err = sub.handler(p); err != nil {
m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
if eh != nil {
_ = eh(p)
} else if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, err.Error())
}
} else if sub.opts.AutoAck {
if err = ms.Ack(); err != nil {
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
}
}
// single processing
case sub.handler != nil:
for _, p := range ms {
if err = sub.handler(p); err != nil {
p.SetError(err)
if eh != nil {
_ = eh(p)
} else if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, err.Error())
}
} else if sub.opts.AutoAck {
} else {
if sub.opts.AutoAck {
if err = p.Ack(); err != nil {
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
} else {
m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
}
} else {
m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
}
}
m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(-1)
m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(-1)
}
}
te := time.Since(ts)
m.opts.Meter.Summary(PublishMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Histogram(PublishMessageDurationSeconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Histogram(SubscribeMessageDurationSeconds, "endpoint", t).Update(te.Seconds())
}
}
return nil
}
func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
m.RLock()
if !m.connected {
m.RUnlock()
return nil, ErrNotConnected
}
m.RUnlock()
sid, err := id.New()
if err != nil {
return nil, err
}
options := NewSubscribeOptions(opts...)
sub := &memorySubscriber{
exit: make(chan bool, 1),
id: sid,
topic: topic,
batchhandler: handler,
opts: options,
ctx: ctx,
}
m.Lock()
m.subscribers[topic] = append(m.subscribers[topic], sub)
m.Unlock()
go func() {
<-sub.exit
m.Lock()
newSubscribers := make([]*memorySubscriber, 0, len(m.subscribers)-1)
for _, sb := range m.subscribers[topic] {
if sb.id == sub.id {
continue
}
newSubscribers = append(newSubscribers, sb)
}
m.subscribers[topic] = newSubscribers
m.Unlock()
}()
return sub, nil
}
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...SubscribeOption) (Subscriber, error) {
m.RLock()
if !m.connected {
m.RUnlock()
@@ -286,38 +232,41 @@ func (m *memoryBroker) Name() string {
return m.opts.Name
}
func (m *memoryEvent) Topic() string {
type memoryMessage struct {
err error
body interface{}
topic string
opts PublishOptions
ctx context.Context
}
func (m *memoryMessage) Topic() string {
return m.topic
}
func (m *memoryEvent) Message() *Message {
switch v := m.message.(type) {
case *Message:
return v
case []byte:
msg := &Message{}
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", err)
}
return nil
}
return msg
}
func (m *memoryMessage) Body() interface{} {
return m.body
}
func (m *memoryMessage) Ack() error {
return nil
}
func (m *memoryEvent) Ack() error {
return nil
}
func (m *memoryEvent) Error() error {
func (m *memoryMessage) Error() error {
return m.err
}
func (m *memoryEvent) SetError(err error) {
m.err = err
func (m *memoryMessage) Context() context.Context {
return m.ctx
}
type memorySubscriber struct {
ctx context.Context
exit chan bool
handler interface{}
id string
topic string
opts SubscribeOptions
}
func (m *memorySubscriber) Options() SubscribeOptions {
@@ -334,7 +283,7 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
}
// NewBroker return new memory broker
func NewBroker(opts ...Option) Broker {
func NewBroker(opts ...Option) *memoryBroker {
return &memoryBroker{
opts: NewOptions(opts...),
subscribers: make(map[string][]*memorySubscriber),

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"testing"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
func TestMemoryBatchBroker(t *testing.T) {

View File

@@ -5,11 +5,31 @@ import (
"crypto/tls"
"time"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/register"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/register"
"go.unistack.org/micro/v4/tracer"
)
var (
// PublishMessageDurationSeconds specifies meter metric name
PublishMessageDurationSeconds = "publish_message_duration_seconds"
// PublishMessageLatencyMicroseconds specifies meter metric name
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
// PublishMessageTotal specifies meter metric name
PublishMessageTotal = "publish_message_total"
// PublishMessageInflight specifies meter metric name
PublishMessageInflight = "publish_message_inflight"
// SubscribeMessageDurationSeconds specifies meter metric name
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
// SubscribeMessageLatencyMicroseconds specifies meter metric name
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
// SubscribeMessageTotal specifies meter metric name
SubscribeMessageTotal = "subscribe_message_total"
// SubscribeMessageInflight specifies meter metric name
SubscribeMessageInflight = "subscribe_message_inflight"
)
// Options struct
@@ -18,8 +38,8 @@ type Options struct {
Tracer tracer.Tracer
// Register can be used for clustering
Register register.Register
// Codec holds the codec for marshal/unmarshal
Codec codec.Codec
// Codecs holds the codec for marshal/unmarshal
Codecs map[string]codec.Codec
// Logger used for logging
Logger logger.Logger
// Meter used for metrics
@@ -29,15 +49,16 @@ type Options struct {
// TLSConfig holds tls.TLSConfig options
TLSConfig *tls.Config
// ErrorHandler used when broker can't unmarshal incoming message
ErrorHandler Handler
// BatchErrorHandler used when broker can't unmashal incoming messages
BatchErrorHandler BatchHandler
ErrorHandler func(Message)
// Name holds the broker name
Name string
// Addrs holds the broker address
Addrs []string
}
// Option func
type Option func(*Options)
// NewOptions create new Options
func NewOptions(opts ...Option) Options {
options := Options{
@@ -45,7 +66,7 @@ func NewOptions(opts ...Option) Options {
Logger: logger.DefaultLogger,
Context: context.Background(),
Meter: meter.DefaultMeter,
Codec: codec.DefaultCodec,
Codecs: make(map[string]codec.Codec),
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
@@ -61,6 +82,32 @@ func Context(ctx context.Context) Option {
}
}
// MessageOption func
type MessageOption func(*MessageOptions)
// MessageOptions struct
type MessageOptions struct {
Metadata metadata.Metadata
ContentType string
}
// MessageMetadata pass additional message metadata
func MessageMetadata(md metadata.Metadata) MessageOption {
return func(o *MessageOptions) {
o.Metadata = md
}
}
// MessageContentType pass ContentType for message data
func MessageContentType(ct string) MessageOption {
return func(o *MessageOptions) {
o.ContentType = ct
}
}
// PublishOption func
type PublishOption func(*PublishOptions)
// PublishOptions struct
type PublishOptions struct {
// Context holds external options
@@ -85,11 +132,9 @@ type SubscribeOptions struct {
// Context holds external options
Context context.Context
// ErrorHandler used when broker can't unmarshal incoming message
ErrorHandler Handler
// BatchErrorHandler used when broker can't unmashal incoming messages
BatchErrorHandler BatchHandler
// Group holds consumer group
Group string
ErrorHandler func(Message)
// QueueGroup holds consumer group
QueueGroup string
// AutoAck flag specifies auto ack of incoming message when no error happens
AutoAck bool
// BodyOnly flag specifies that message contains only body bytes without header
@@ -100,12 +145,6 @@ type SubscribeOptions struct {
BatchWait time.Duration
}
// Option func
type Option func(*Options)
// PublishOption func
type PublishOption func(*PublishOptions)
// PublishBodyOnly publish only body of the message
func PublishBodyOnly(b bool) PublishOption {
return func(o *PublishOptions) {
@@ -129,59 +168,29 @@ func Addrs(addrs ...string) Option {
// Codec sets the codec used for encoding/decoding used where
// a broker does not support headers
func Codec(c codec.Codec) Option {
// Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c codec.Codec) Option {
return func(o *Options) {
o.Codec = c
o.Codecs[contentType] = c
}
}
// ErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func ErrorHandler(h Handler) Option {
func ErrorHandler(h func(Message)) Option {
return func(o *Options) {
o.ErrorHandler = h
}
}
// BatchErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func BatchErrorHandler(h BatchHandler) Option {
return func(o *Options) {
o.BatchErrorHandler = h
}
}
// SubscribeErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func SubscribeErrorHandler(h Handler) SubscribeOption {
func SubscribeErrorHandler(h func(Message)) SubscribeOption {
return func(o *SubscribeOptions) {
o.ErrorHandler = h
}
}
// SubscribeBatchErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func SubscribeBatchErrorHandler(h BatchHandler) SubscribeOption {
return func(o *SubscribeOptions) {
o.BatchErrorHandler = h
}
}
// Queue sets the subscribers queue
// Deprecated
func Queue(name string) SubscribeOption {
return func(o *SubscribeOptions) {
o.Group = name
}
}
// SubscribeGroup sets the name of the queue to share messages on
func SubscribeGroup(name string) SubscribeOption {
return func(o *SubscribeOptions) {
o.Group = name
}
}
// Register sets register option
func Register(r register.Register) Option {
return func(o *Options) {
@@ -224,6 +233,21 @@ func Name(n string) Option {
}
}
// SubscribeOption func signature
type SubscribeOption func(*SubscribeOptions)
// NewSubscribeOptions creates new SubscribeOptions
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
options := SubscribeOptions{
AutoAck: true,
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// SubscribeContext set context
func SubscribeContext(ctx context.Context) SubscribeOption {
return func(o *SubscribeOptions) {
@@ -231,14 +255,6 @@ func SubscribeContext(ctx context.Context) SubscribeOption {
}
}
// DisableAutoAck disables auto ack
// Deprecated
func DisableAutoAck() SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = false
}
}
// SubscribeAutoAck contol auto acking of messages
// after they have been handled.
func SubscribeAutoAck(b bool) SubscribeOption {
@@ -268,17 +284,16 @@ func SubscribeBatchWait(td time.Duration) SubscribeOption {
}
}
// SubscribeOption func
type SubscribeOption func(*SubscribeOptions)
// NewSubscribeOptions creates new SubscribeOptions
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
options := SubscribeOptions{
AutoAck: true,
Context: context.Background(),
// SubscribeQueueGroup sets the shared queue name distributed messages across subscribers
func SubscribeQueueGroup(n string) SubscribeOption {
return func(o *SubscribeOptions) {
o.QueueGroup = n
}
}
// SubscribeAutoAck control auto ack processing for handler
func SubscribeAuthAck(b bool) SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = b
}
for _, o := range opts {
o(&options)
}
return options
}

98
broker/subscriber.go Normal file
View File

@@ -0,0 +1,98 @@
package broker
import (
"fmt"
"reflect"
"strings"
"unicode"
"unicode/utf8"
)
const (
subSig = "func(context.Context, interface{}) error"
batchSubSig = "func([]context.Context, []interface{}) error"
)
// Precompute the reflect type for error. Can't use error directly
// because Typeof takes an empty interface value. This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// ValidateSubscriber func signature
func ValidateSubscriber(sub interface{}) error {
typ := reflect.TypeOf(sub)
var argType reflect.Type
switch typ.Kind() {
case reflect.Func:
name := "Func"
switch typ.NumIn() {
case 1: // func(Message) error
case 2: // func(context.Context, Message) error or func(context.Context, []Message) error
argType = typ.In(2)
// if sub.Options().Batch {
if argType.Kind() != reflect.Slice {
return fmt.Errorf("subscriber %v dont have required signature %s", name, batchSubSig)
}
if strings.Compare(fmt.Sprintf("%v", argType), "[]interface{}") == 0 {
return fmt.Errorf("subscriber %v dont have required signaure %s", name, batchSubSig)
}
// }
default:
return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s or %s", name, typ.NumIn(), subSig, batchSubSig)
}
if !isExportedOrBuiltinType(argType) {
return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
}
if typ.NumOut() != 1 {
return fmt.Errorf("subscriber %v has wrong number of return values: %v require signature %s or %s",
name, typ.NumOut(), subSig, batchSubSig)
}
if returnType := typ.Out(0); returnType != typeOfError {
return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
}
default:
hdlr := reflect.ValueOf(sub)
name := reflect.Indirect(hdlr).Type().Name()
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
switch method.Type.NumIn() {
case 3:
argType = method.Type.In(2)
default:
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s or %s",
name, method.Name, method.Type.NumIn(), subSig, batchSubSig)
}
if !isExportedOrBuiltinType(argType) {
return fmt.Errorf("%v argument type not exported: %v", name, argType)
}
if method.Type.NumOut() != 1 {
return fmt.Errorf(
"subscriber %v.%v has wrong number of return values: %v require signature %s or %s",
name, method.Name, method.Type.NumOut(), subSig, batchSubSig)
}
if returnType := method.Type.Out(0); returnType != typeOfError {
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
}
}
}
return nil
}

View File

@@ -5,7 +5,7 @@ import (
"math"
"time"
"go.unistack.org/micro/v3/util/backoff"
"go.unistack.org/micro/v4/util/backoff"
)
// BackoffFunc is the backoff call func

View File

@@ -1,19 +1,18 @@
// Package client is an interface for an RPC client
package client // import "go.unistack.org/micro/v3/client"
package client // import "go.unistack.org/micro/v4/client"
import (
"context"
"time"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/codec"
)
var (
// DefaultClient is the global default client
DefaultClient = NewClient()
// DefaultContentType is the default content-type if not specified
DefaultContentType = "application/json"
DefaultContentType = ""
// DefaultBackoff is the default backoff function for retries (minimum 10 millisecond and maximum 5 second)
DefaultBackoff = BackoffInterval(10*time.Millisecond, 5*time.Second)
// DefaultRetry is the default check-for-retry function for retries
@@ -35,23 +34,12 @@ type Client interface {
Name() string
Init(opts ...Option) error
Options() Options
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message
NewRequest(service string, endpoint string, req interface{}, opts ...RequestOption) Request
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
BatchPublish(ctx context.Context, msg []Message, opts ...PublishOption) error
String() string
}
// Message is the interface for publishing asynchronously
type Message interface {
Topic() string
Payload() interface{}
ContentType() string
Metadata() metadata.Metadata
}
// Request is the interface for a synchronous request used by Call or Stream
type Request interface {
// The service to call
@@ -68,16 +56,22 @@ type Request interface {
Codec() codec.Codec
// indicates whether the request will be a streaming one rather than unary
Stream() bool
// Header data
// Header() metadata.Metadata
}
// Response is the response received from a service
type Response interface {
// Read the response
Codec() codec.Codec
// read the header
Header() metadata.Metadata
// The content type
// ContentType() string
// Header data
// Header() metadata.Metadata
// Read the undecoded response
Read() ([]byte, error)
// The unencoded request body
// Body() interface{}
}
// Stream is the interface for a bidirectional synchronous stream

View File

@@ -4,8 +4,8 @@ import (
"context"
"sort"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/router"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/router"
)
// LookupFunc is used to lookup routes for a service

View File

@@ -5,11 +5,10 @@ import (
"fmt"
"time"
"go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/selector"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/selector"
)
// DefaultCodecs will be used to encode/decode data
@@ -285,6 +284,9 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt
ch := make(chan error, callOpts.Retries)
var gerr error
ts := time.Now()
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Inc()
for i := 0; i <= callOpts.Retries; i++ {
go func() {
ch <- call(i)
@@ -312,6 +314,16 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt
}
}
if gerr != nil {
n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc()
} else {
n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc()
}
n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Dec()
te := time.Since(ts)
n.opts.Meter.Summary(ClientRequestLatencyMicroseconds, "endpoint", endpoint).Update(te.Seconds())
n.opts.Meter.Histogram(ClientRequestDurationSeconds, "endpoint", endpoint).Update(te.Seconds())
return gerr
}
@@ -323,11 +335,6 @@ func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts
return &noopRequest{service: service, endpoint: endpoint}
}
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
options := NewMessageOptions(append([]MessageOption{MessageContentType(n.opts.ContentType)}, opts...)...)
return &noopMessage{topic: topic, payload: msg, opts: options}
}
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
var err error
@@ -414,7 +421,15 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
node := next()
// ts := time.Now()
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Inc()
stream, cerr := n.stream(ctx, node, req, callOpts)
if cerr != nil {
n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc()
} else {
n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc()
}
// record the result of the call to inform future routing decisions
if verr := n.opts.Selector.Record(node, cerr); verr != nil {
@@ -468,64 +483,6 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
return nil, grr
}
func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (Stream, error) {
func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (*noopStream, error) {
return &noopStream{}, nil
}
func (n *noopClient) BatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
return n.publish(ctx, ps, opts...)
}
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
return n.publish(ctx, []Message{p}, opts...)
}
func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishOption) error {
options := NewPublishOptions(opts...)
msgs := make([]*broker.Message, 0, len(ps))
for _, p := range ps {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(0)
}
md[metadata.HeaderContentType] = p.ContentType()
topic := p.Topic()
// get the exchange
if len(options.Exchange) > 0 {
topic = options.Exchange
}
md[metadata.HeaderTopic] = topic
var body []byte
// passed in raw data
if d, ok := p.Payload().(*codec.Frame); ok {
body = d.Data
} else {
// use codec for payload
cf, err := n.newCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// set the body
b, err := cf.Marshal(p.Payload())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
body = b
}
msgs = append(msgs, &broker.Message{Header: md, Body: body})
}
return n.opts.Broker.BatchPublish(ctx, msgs,
broker.PublishContext(options.Context),
broker.PublishBodyOnly(options.BodyOnly),
)
}

View File

@@ -6,17 +6,27 @@ import (
"net"
"time"
"go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/register"
"go.unistack.org/micro/v3/router"
"go.unistack.org/micro/v3/selector"
"go.unistack.org/micro/v3/selector/random"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/network/transport"
"go.unistack.org/micro/v4/register"
"go.unistack.org/micro/v4/router"
"go.unistack.org/micro/v4/selector"
"go.unistack.org/micro/v4/selector/random"
"go.unistack.org/micro/v4/tracer"
)
var (
// ClientRequestDurationSeconds specifies meter metric name
ClientRequestDurationSeconds = "client_request_duration_seconds"
// ClientRequestLatencyMicroseconds specifies meter metric name
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
// ClientRequestTotal specifies meter metric name
ClientRequestTotal = "client_request_total"
// ClientRequestInflight specifies meter metric name
ClientRequestInflight = "client_request_inflight"
)
// Options holds client options
@@ -29,8 +39,6 @@ type Options struct {
Logger logger.Logger
// Tracer used for tracing
Tracer tracer.Tracer
// Broker used to publish messages
Broker broker.Broker
// Meter used for metrics
Meter meter.Meter
// Context is used for external options
@@ -101,7 +109,7 @@ type CallOptions struct {
// RequestMetadata holds additional metadata for call
RequestMetadata metadata.Metadata
// ResponseMetadata holds additional metadata from call
ResponseMetadata metadata.Metadata
ResponseMetadata *metadata.Metadata
// DialTimeout dial timeout
DialTimeout time.Duration
// Retries specifies retries num
@@ -199,7 +207,6 @@ func NewOptions(opts ...Option) Options {
PoolTTL: DefaultPoolTTL,
Selector: random.NewSelector(),
Logger: logger.DefaultLogger,
Broker: broker.DefaultBroker,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Router: router.DefaultRouter,
@@ -213,13 +220,6 @@ func NewOptions(opts ...Option) Options {
return options
}
// Broker to be used for pub/sub
func Broker(b broker.Broker) Option {
return func(o *Options) {
o.Broker = b
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
@@ -482,7 +482,7 @@ func WithRetries(i int) CallOption {
}
// WithResponseMetadata is a CallOption which adds metadata.Metadata to Options.CallOptions
func WithResponseMetadata(md metadata.Metadata) CallOption {
func WithResponseMetadata(md *metadata.Metadata) CallOption {
return func(o *CallOptions) {
o.ResponseMetadata = md
}

View File

@@ -3,7 +3,7 @@ package client
import (
"context"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v4/errors"
)
// RetryFunc that returning either false or a non-nil error will result in the call not being retried

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"testing"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v4/errors"
)
func TestRetryAlways(t *testing.T) {

View File

@@ -1,7 +1,7 @@
package client
import (
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v4/codec"
)
type testRequest struct {

View File

@@ -1,11 +1,11 @@
// Package codec is an interface for encoding messages
package codec // import "go.unistack.org/micro/v3/codec"
package codec // import "go.unistack.org/micro/v4/codec"
import (
"errors"
"io"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
// Message types
@@ -84,3 +84,24 @@ func MarshalAppend(buf []byte, c Codec, v interface{}, opts ...Option) ([]byte,
return append(buf, mbuf...), nil
}
// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return *m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawMessage UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Unistack LLC
// Copyright 2021-2023 Unistack LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ syntax = "proto3";
package micro.codec;
option cc_enable_arenas = true;
option go_package = "go.unistack.org/micro/v3/codec;codec";
option go_package = "go.unistack.org/micro/v4/codec;codec";
option java_multiple_files = true;
option java_outer_classname = "MicroCodec";
option java_package = "micro.codec";

View File

@@ -3,9 +3,9 @@ package codec
import (
"context"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/tracer"
)
// Option func

View File

@@ -1,5 +1,5 @@
// Package config is an interface for dynamic configuration.
package config // import "go.unistack.org/micro/v3/config"
package config // import "go.unistack.org/micro/v4/config"
import (
"context"
@@ -124,33 +124,12 @@ func Validate(ctx context.Context, cfg interface{}) error {
}
var (
// DefaultAfterLoad default func that runs after config load
DefaultAfterLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterLoad {
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultAfterSave default func that runs after config save
DefaultAfterSave = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterSave {
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeLoad default func that runs before config load
// DefaultBeforeLoad default func that runs before config Load
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeLoad {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s BeforeLoad err: %v", c.String(), err)
if !c.Options().AllowFail {
@@ -160,9 +139,27 @@ var (
}
return nil
}
// DefaultBeforeSave default func that runs befora config save
// DefaultAfterLoad default func that runs after config Load
DefaultAfterLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterLoad {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeSave default func that runs befora config Save
DefaultBeforeSave = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s BeforeSave err: %v", c.String(), err)
if !c.Options().AllowFail {
@@ -172,4 +169,49 @@ var (
}
return nil
}
// DefaultAfterSave default func that runs after config Save
DefaultAfterSave = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeInit default func that runs befora config Init
DefaultBeforeInit = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeInit {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s BeforeInit err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultAfterInit default func that runs after config Init
DefaultAfterInit = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterInit err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
)

View File

@@ -5,9 +5,11 @@ import (
"reflect"
"strconv"
"strings"
"time"
"github.com/imdario/mergo"
rutil "go.unistack.org/micro/v3/util/reflect"
rutil "go.unistack.org/micro/v4/util/reflect"
mtime "go.unistack.org/micro/v4/util/time"
)
type defaultConfig struct {
@@ -22,11 +24,20 @@ func (c *defaultConfig) Init(opts ...Option) error {
for _, o := range opts {
o(&c.opts)
}
if err := DefaultBeforeInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
return err
}
if err := DefaultAfterInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
return err
}
return nil
}
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
if err := DefaultBeforeLoad(ctx, c); err != nil {
if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
return err
}
@@ -49,21 +60,20 @@ func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
if !c.opts.AllowFail {
return err
}
return DefaultAfterLoad(ctx, c)
if err = DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
return err
}
}
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
err = mergo.Merge(dst, src, mopts...)
}
if err != nil {
c.opts.Logger.Errorf(ctx, "default load error: %v", err)
if !c.opts.AllowFail {
return err
}
if err != nil && !c.opts.AllowFail {
return err
}
if err := DefaultAfterLoad(ctx, c); err != nil {
if err := DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
return err
}
@@ -75,6 +85,7 @@ func fillValue(value reflect.Value, val string) error {
if !rutil.IsEmpty(value) {
return nil
}
switch value.Kind() {
case reflect.Map:
t := value.Type()
@@ -151,11 +162,26 @@ func fillValue(value reflect.Value, val string) error {
}
value.Set(reflect.ValueOf(int32(v)))
case reflect.Int64:
v, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
switch {
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
v, err := time.ParseDuration(val)
if err != nil {
return err
}
value.Set(reflect.ValueOf(v))
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v4/util/time":
v, err := mtime.ParseDuration(val)
if err != nil {
return err
}
value.SetInt(int64(v))
default:
v, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
value.Set(reflect.ValueOf(v))
}
value.Set(reflect.ValueOf(v))
case reflect.Uint:
v, err := strconv.ParseUint(val, 10, 0)
if err != nil {

View File

@@ -4,15 +4,20 @@ import (
"context"
"fmt"
"testing"
"time"
"go.unistack.org/micro/v3/config"
"go.unistack.org/micro/v4/config"
mtime "go.unistack.org/micro/v4/util/time"
)
type cfg struct {
StringValue string `default:"string_value"`
IgnoreValue string `json:"-"`
StructValue *cfgStructValue
IntValue int `default:"99"`
StringValue string `default:"string_value"`
IgnoreValue string `json:"-"`
StructValue *cfgStructValue
IntValue int `default:"99"`
DurationValue time.Duration `default:"10s"`
MDurationValue mtime.Duration `default:"10s"`
MapValue map[string]bool `default:"key1=true,key2=false"`
}
type cfgStructValue struct {
@@ -63,6 +68,9 @@ func TestDefault(t *testing.T) {
if conf.StringValue != "after_load" {
t.Fatal("AfterLoad option not working")
}
if len(conf.MapValue) != 2 {
t.Fatalf("map value invalid: %#+v\n", conf.MapValue)
}
_ = conf
// t.Logf("%#+v\n", conf)
}

View File

@@ -4,10 +4,10 @@ import (
"context"
"time"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/tracer"
)
// Options hold the config options
@@ -28,14 +28,18 @@ type Options struct {
Name string
// StructTag name
StructTag string
// BeforeSave contains slice of funcs that runs before save
// BeforeSave contains slice of funcs that runs before Save
BeforeSave []func(context.Context, Config) error
// AfterLoad contains slice of funcs that runs after load
AfterLoad []func(context.Context, Config) error
// BeforeLoad contains slice of funcs that runs before load
BeforeLoad []func(context.Context, Config) error
// AfterSave contains slice of funcs that runs after save
// AfterSave contains slice of funcs that runs after Save
AfterSave []func(context.Context, Config) error
// BeforeLoad contains slice of funcs that runs before Load
BeforeLoad []func(context.Context, Config) error
// AfterLoad contains slice of funcs that runs after Load
AfterLoad []func(context.Context, Config) error
// BeforeInit contains slice of funcs that runs before Init
BeforeInit []func(context.Context, Config) error
// AfterInit contains slice of funcs that runs after Init
AfterInit []func(context.Context, Config) error
// AllowFail flag to allow fail in config source
AllowFail bool
}
@@ -131,6 +135,20 @@ func AllowFail(b bool) Option {
}
}
// BeforeInit run funcs before config Init
func BeforeInit(fn ...func(context.Context, Config) error) Option {
return func(o *Options) {
o.BeforeInit = fn
}
}
// AfterInit run funcs after config Init
func AfterInit(fn ...func(context.Context, Config) error) Option {
return func(o *Options) {
o.AfterInit = fn
}
}
// BeforeLoad run funcs before config load
func BeforeLoad(fn ...func(context.Context, Config) error) Option {
return func(o *Options) {

View File

@@ -1,6 +1,6 @@
// Package errors provides a way to return detailed information
// for an RPC request error. The error is normally JSON encoded.
package errors // import "go.unistack.org/micro/v3/errors"
package errors // import "go.unistack.org/micro/v4/errors"
import (
"bytes"

View File

@@ -1,4 +1,4 @@
// Copyright 2021 Unistack LLC
// Copyright 2021-2023 Unistack LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ syntax = "proto3";
package micro.errors;
option cc_enable_arenas = true;
option go_package = "go.unistack.org/micro/v3/errors;errors";
option go_package = "go.unistack.org/micro/v4/errors;errors";
option java_multiple_files = true;
option java_outer_classname = "MicroErrors";
option java_package = "micro.errors";

View File

@@ -1,27 +0,0 @@
package micro
import (
"context"
"go.unistack.org/micro/v3/client"
)
// Event is used to publish messages to a topic
type Event interface {
// Publish publishes a message to the event topic
Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error
}
type event struct {
c client.Client
topic string
}
// NewEvent creates a new event publisher
func NewEvent(topic string, c client.Client) Event {
return &event{c, topic}
}
func (e *event) Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error {
return e.c.Publish(ctx, e.c.NewMessage(e.topic, msg), opts...)
}

View File

@@ -8,6 +8,7 @@ import (
)
func TestDeps(t *testing.T) {
t.Skip()
d := &dag.AcyclicGraph{}
v0 := d.Add(&node{"v0"})

View File

@@ -6,12 +6,12 @@ import (
"sync"
"github.com/silas/dag"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/util/id"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/store"
"go.unistack.org/micro/v4/util/id"
)
type microFlow struct {

View File

@@ -1,5 +1,5 @@
// Package flow is an interface used for saga pattern microservice workflow
package flow // import "go.unistack.org/micro/v3/flow"
package flow // import "go.unistack.org/micro/v4/flow"
import (
"context"
@@ -7,7 +7,7 @@ import (
"sync"
"sync/atomic"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
var (

View File

@@ -4,11 +4,11 @@ import (
"context"
"time"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/store"
"go.unistack.org/micro/v4/tracer"
)
// Option func

126
fsm/default.go Normal file
View File

@@ -0,0 +1,126 @@
package fsm
import (
"context"
"fmt"
"sync"
)
type state struct {
body interface{}
name string
}
var _ State = &state{}
func (s *state) Name() string {
return s.name
}
func (s *state) Body() interface{} {
return s.body
}
// fsm is a finite state machine
type fsm struct {
statesMap map[string]StateFunc
current string
statesOrder []string
opts Options
mu sync.Mutex
}
// NewFSM creates a new finite state machine having the specified initial state
// with specified options
func NewFSM(opts ...Option) *fsm {
return &fsm{
statesMap: map[string]StateFunc{},
opts: NewOptions(opts...),
}
}
// Current returns the current state
func (f *fsm) Current() string {
f.mu.Lock()
s := f.current
f.mu.Unlock()
return s
}
// Current returns the current state
func (f *fsm) Reset() {
f.mu.Lock()
f.current = f.opts.Initial
f.mu.Unlock()
}
// State adds state to fsm
func (f *fsm) State(state string, fn StateFunc) {
f.mu.Lock()
f.statesMap[state] = fn
f.statesOrder = append(f.statesOrder, state)
f.mu.Unlock()
}
// Start runs state machine with provided data
func (f *fsm) Start(ctx context.Context, args interface{}, opts ...Option) (interface{}, error) {
var err error
f.mu.Lock()
options := f.opts
for _, opt := range opts {
opt(&options)
}
sopts := []StateOption{StateDryRun(options.DryRun)}
cstate := options.Initial
states := make(map[string]StateFunc, len(f.statesMap))
for k, v := range f.statesMap {
states[k] = v
}
f.current = cstate
f.mu.Unlock()
var s State
s = &state{name: cstate, body: args}
nstate := s.Name()
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
fn, ok := states[nstate]
if !ok {
return nil, fmt.Errorf(`state "%s" %w`, nstate, ErrInvalidState)
}
f.mu.Lock()
f.current = nstate
f.mu.Unlock()
// wrap the handler func
for i := len(options.Wrappers); i > 0; i-- {
fn = options.Wrappers[i-1](fn)
}
s, err = fn(ctx, s, sopts...)
switch {
case err != nil:
return s.Body(), err
case s.Name() == StateEnd:
return s.Body(), nil
case s.Name() == "":
for idx := range f.statesOrder {
if f.statesOrder[idx] == nstate && len(f.statesOrder) > idx+1 {
nstate = f.statesOrder[idx+1]
}
}
default:
nstate = s.Name()
}
}
}
}

View File

@@ -1,10 +1,8 @@
package fsm // import "go.unistack.org/micro/v3/fsm"
package fsm // import "go.unistack.org/micro/v4/fsm"
import (
"context"
"errors"
"fmt"
"sync"
)
var (
@@ -12,170 +10,20 @@ var (
StateEnd = "end"
)
// Options struct holding fsm options
type Options struct {
// DryRun mode
DryRun bool
// Initial state
Initial string
// HooksBefore func slice runs in order before state
HooksBefore []HookBeforeFunc
// HooksAfter func slice runs in order after state
HooksAfter []HookAfterFunc
type State interface {
Name() string
Body() interface{}
}
// HookBeforeFunc func signature
type HookBeforeFunc func(ctx context.Context, state string, args interface{})
// HookAfterFunc func signature
type HookAfterFunc func(ctx context.Context, state string, args interface{})
// Option func signature
type Option func(*Options)
// StateOptions holds state options
type StateOptions struct {
DryRun bool
}
// StateDryRun says that state executes in dry run mode
func StateDryRun(b bool) StateOption {
return func(o *StateOptions) {
o.DryRun = b
}
}
// StateOption func signature
type StateOption func(*StateOptions)
// InitialState sets init state for state machine
func InitialState(initial string) Option {
return func(o *Options) {
o.Initial = initial
}
}
// HookBefore provides hook func slice
func HookBefore(fns ...HookBeforeFunc) Option {
return func(o *Options) {
o.HooksBefore = fns
}
}
// HookAfter provides hook func slice
func HookAfter(fns ...HookAfterFunc) Option {
return func(o *Options) {
o.HooksAfter = fns
}
}
// StateWrapper wraps the StateFunc and returns the equivalent
type StateWrapper func(StateFunc) StateFunc
// StateFunc called on state transition and return next step and error
type StateFunc func(ctx context.Context, args interface{}, opts ...StateOption) (string, interface{}, error)
type StateFunc func(ctx context.Context, state State, opts ...StateOption) (State, error)
// FSM is a finite state machine
type FSM struct {
mu sync.Mutex
statesMap map[string]StateFunc
statesOrder []string
opts *Options
current string
}
// New creates a new finite state machine having the specified initial state
// with specified options
func New(opts ...Option) *FSM {
options := &Options{}
for _, opt := range opts {
opt(options)
}
return &FSM{
statesMap: map[string]StateFunc{},
opts: options,
}
}
// Current returns the current state
func (f *FSM) Current() string {
f.mu.Lock()
defer f.mu.Unlock()
return f.current
}
// Current returns the current state
func (f *FSM) Reset() {
f.mu.Lock()
f.current = f.opts.Initial
f.mu.Unlock()
}
// State adds state to fsm
func (f *FSM) State(state string, fn StateFunc) {
f.mu.Lock()
f.statesMap[state] = fn
f.statesOrder = append(f.statesOrder, state)
f.mu.Unlock()
}
// Init initialize fsm and check states
// Start runs state machine with provided data
func (f *FSM) Start(ctx context.Context, args interface{}, opts ...Option) (interface{}, error) {
var err error
var ok bool
var fn StateFunc
var nstate string
f.mu.Lock()
options := f.opts
for _, opt := range opts {
opt(options)
}
sopts := []StateOption{StateDryRun(options.DryRun)}
cstate := options.Initial
states := make(map[string]StateFunc, len(f.statesMap))
for k, v := range f.statesMap {
states[k] = v
}
f.current = cstate
f.mu.Unlock()
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
fn, ok = states[cstate]
if !ok {
return nil, fmt.Errorf(`state "%s" %w`, cstate, ErrInvalidState)
}
f.mu.Lock()
f.current = cstate
f.mu.Unlock()
for _, fn := range options.HooksBefore {
fn(ctx, cstate, args)
}
nstate, args, err = fn(ctx, args, sopts...)
for _, fn := range options.HooksAfter {
fn(ctx, cstate, args)
}
switch {
case err != nil:
return args, err
case nstate == StateEnd:
return args, nil
case nstate == "":
for idx := range f.statesOrder {
if f.statesOrder[idx] == cstate && len(f.statesOrder) > idx+1 {
nstate = f.statesOrder[idx+1]
}
}
}
cstate = nstate
}
}
type FSM interface {
Start(context.Context, interface{}, ...Option) (interface{}, error)
Current() string
Reset()
State(string, StateFunc)
}

View File

@@ -1,63 +1,72 @@
package fsm
import (
"bytes"
"context"
"fmt"
"testing"
"go.unistack.org/micro/v4/logger"
)
func TestFSMStart(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
pfb := func(_ context.Context, state string, _ interface{}) {
fmt.Fprintf(buf, "before state %s\n", state)
if err := logger.DefaultLogger.Init(); err != nil {
t.Fatal(err)
}
pfa := func(_ context.Context, state string, _ interface{}) {
fmt.Fprintf(buf, "after state %s\n", state)
wrapper := func(next StateFunc) StateFunc {
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
sctx = logger.NewContext(sctx, logger.Fields("state", s.Name()))
return next(sctx, s, opts...)
}
}
f := New(InitialState("1"), HookBefore(pfb), HookAfter(pfa))
f1 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
args := req.(map[string]interface{})
f := NewFSM(InitialState("1"), WrapState(wrapper))
f1 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
_, ok := logger.FromContext(sctx)
if !ok {
t.Fatal("f1 context does not have logger")
}
args := s.Body().(map[string]interface{})
if v, ok := args["request"].(string); !ok || v == "" {
return "", nil, fmt.Errorf("empty request")
return nil, fmt.Errorf("empty request")
}
return "2", map[string]interface{}{"response": "test2"}, nil
return &state{name: "", body: map[string]interface{}{"response": "state1"}}, nil
}
f2 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
args := req.(map[string]interface{})
if v, ok := args["response"].(string); !ok || v == "" {
return "", nil, fmt.Errorf("empty response")
f2 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
_, ok := logger.FromContext(sctx)
if !ok {
t.Fatal("f2 context does not have logger")
}
return "", map[string]interface{}{"response": "test"}, nil
args := s.Body().(map[string]interface{})
if v, ok := args["response"].(string); !ok || v == "" {
return nil, fmt.Errorf("empty response")
}
return &state{name: "", body: map[string]interface{}{"response": "state2"}}, nil
}
f3 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
args := req.(map[string]interface{})
if v, ok := args["response"].(string); !ok || v == "" {
return "", nil, fmt.Errorf("empty response")
f3 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
_, ok := logger.FromContext(sctx)
if !ok {
t.Fatal("f3 context does not have logger")
}
return StateEnd, map[string]interface{}{"response": "test_last"}, nil
args := s.Body().(map[string]interface{})
if v, ok := args["response"].(string); !ok || v == "" {
return nil, fmt.Errorf("empty response")
}
return &state{name: StateEnd, body: map[string]interface{}{"response": "state3"}}, nil
}
f.State("1", f1)
f.State("2", f2)
f.State("3", f3)
rsp, err := f.Start(ctx, map[string]interface{}{"request": "test1"})
rsp, err := f.Start(ctx, map[string]interface{}{"request": "state"})
if err != nil {
t.Fatal(err)
}
args := rsp.(map[string]interface{})
if v, ok := args["response"].(string); !ok || v == "" {
t.Fatalf("nil rsp: %#+v", args)
} else if v != "test_last" {
} else if v != "state3" {
t.Fatalf("invalid rsp %#+v", args)
}
if !bytes.Contains(buf.Bytes(), []byte(`before state 1`)) ||
!bytes.Contains(buf.Bytes(), []byte(`before state 2`)) ||
!bytes.Contains(buf.Bytes(), []byte(`after state 1`)) ||
!bytes.Contains(buf.Bytes(), []byte(`after state 2`)) ||
!bytes.Contains(buf.Bytes(), []byte(`after state 3`)) ||
!bytes.Contains(buf.Bytes(), []byte(`after state 3`)) {
t.Fatalf("fsm not works properly or hooks error, buf: %s", buf.Bytes())
}
}

52
fsm/options.go Normal file
View File

@@ -0,0 +1,52 @@
package fsm
// Options struct holding fsm options
type Options struct {
// Initial state
Initial string
// Wrappers runs before state
Wrappers []StateWrapper
// DryRun mode
DryRun bool
}
// Option func signature
type Option func(*Options)
// StateOptions holds state options
type StateOptions struct {
DryRun bool
}
// StateDryRun says that state executes in dry run mode
func StateDryRun(b bool) StateOption {
return func(o *StateOptions) {
o.DryRun = b
}
}
// StateOption func signature
type StateOption func(*StateOptions)
// InitialState sets init state for state machine
func InitialState(initial string) Option {
return func(o *Options) {
o.Initial = initial
}
}
// WrapState adds a state Wrapper to a list of options passed into the fsm
func WrapState(w StateWrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}
// NewOptions returns new Options struct filled by passed Option
func NewOptions(opts ...Option) Options {
options := Options{}
for _, o := range opts {
o(&options)
}
return options
}

22
go.mod
View File

@@ -1,13 +1,19 @@
module go.unistack.org/micro/v3
module go.unistack.org/micro/v4
go 1.16
go 1.20
require (
github.com/google/go-cmp v0.5.7 // indirect
github.com/imdario/mergo v0.3.13
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/imdario/mergo v0.3.15
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35
go.unistack.org/micro-proto/v3 v3.3.1
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5
golang.org/x/sync v0.1.0
golang.org/x/sys v0.7.0
google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0
)
require (
github.com/golang/protobuf v1.5.3 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
)

175
go.sum
View File

@@ -1,160 +1,31 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35 h1:4mohWoM/UGg1BvFFiqSPRl5uwJY3rVV0HQX0ETqauqQ=
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.unistack.org/micro-proto/v3 v3.3.1 h1:nQ0MtWvP2G3QrpOgawVOPhpZZYkq6umTGDqs8FxJYIo=
go.unistack.org/micro-proto/v3 v3.3.1/go.mod h1:cwRyv8uInM2I7EbU7O8Fx2Ls3N90Uw9UCCcq4olOdfE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E=
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -12,10 +12,8 @@ import (
)
type defaultLogger struct {
enc *json.Encoder
logFunc LogFunc
logfFunc LogfFunc
opts Options
enc *json.Encoder
opts Options
sync.RWMutex
}
@@ -27,10 +25,6 @@ func (l *defaultLogger) Init(opts ...Option) error {
}
l.enc = json.NewEncoder(l.opts.Out)
// wrap the Log func
for i := len(l.opts.Wrappers); i > 0; i-- {
l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc)
l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc)
}
l.Unlock()
return nil
}
@@ -47,17 +41,10 @@ func (l *defaultLogger) Clone(opts ...Option) Logger {
o(&oldopts)
}
oldopts.Wrappers = newopts.Wrappers
l.Lock()
cl := &defaultLogger{opts: oldopts, logFunc: l.logFunc, logfFunc: l.logfFunc}
cl := &defaultLogger{opts: oldopts, enc: json.NewEncoder(l.opts.Out)}
l.Unlock()
// wrap the Log func
for i := len(newopts.Wrappers); i > 0; i-- {
cl.logFunc = newopts.Wrappers[i-1].Log(cl.logFunc)
cl.logfFunc = newopts.Wrappers[i-1].Logf(cl.logfFunc)
}
return cl
}
@@ -75,15 +62,17 @@ func (l *defaultLogger) Level(level Level) {
}
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
l.RLock()
nl := &defaultLogger{opts: l.opts, enc: l.enc}
if len(fields) == 0 {
l.RUnlock()
return nl
} else if len(fields)%2 != 0 {
fields = fields[:len(fields)-1]
}
nl.logFunc = l.logFunc
nl.logfFunc = l.logfFunc
nl.opts.Fields = copyFields(l.opts.Fields)
nl.opts.Fields = append(nl.opts.Fields, fields...)
l.RUnlock()
return nl
}
@@ -143,27 +132,27 @@ func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
}
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, InfoLevel, msg, args...)
l.Logf(ctx, InfoLevel, msg, args...)
}
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, ErrorLevel, msg, args...)
l.Logf(ctx, ErrorLevel, msg, args...)
}
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, DebugLevel, msg, args...)
l.Logf(ctx, DebugLevel, msg, args...)
}
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, WarnLevel, msg, args...)
l.Logf(ctx, WarnLevel, msg, args...)
}
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, TraceLevel, msg, args...)
l.Logf(ctx, TraceLevel, msg, args...)
}
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
l.logfFunc(ctx, FatalLevel, msg, args...)
l.Logf(ctx, FatalLevel, msg, args...)
os.Exit(1)
}
@@ -236,8 +225,6 @@ func NewLogger(opts ...Option) Logger {
l := &defaultLogger{
opts: NewOptions(opts...),
}
l.logFunc = l.Log
l.logfFunc = l.Logf
l.enc = json.NewEncoder(l.opts.Out)
return l
}

View File

@@ -1,5 +1,5 @@
// Package logger provides a log interface
package logger // import "go.unistack.org/micro/v3/logger"
package logger // import "go.unistack.org/micro/v4/logger"
import (
"context"

View File

@@ -32,7 +32,33 @@ func TestFields(t *testing.T) {
if err := l.Init(); err != nil {
t.Fatal(err)
}
l.Fields("key", "val").Info(ctx, "message")
nl := l.Fields("key", "val")
nl.Info(ctx, "message")
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
}
}
func TestFromContextWithFields(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
var ok bool
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
nl := l.Fields("key", "val")
ctx = NewContext(ctx, nl)
l, ok = FromContext(ctx)
if !ok {
t.Fatalf("context does not have logger")
}
l.Info(ctx, "message")
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
}
@@ -110,39 +136,3 @@ func TestLogger(t *testing.T) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
}
func TestLoggerWrapper(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(WrapLogger(NewOmitWrapper())); err != nil {
t.Fatal(err)
}
type secret struct {
Name string
Passw string `logger:"omit"`
}
s := &secret{Name: "name", Passw: "secret"}
l.Errorf(ctx, "test %#+v", s)
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
}
}
func TestOmitLoggerWrapper(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewOmitLogger(NewLogger(WithLevel(TraceLevel), WithOutput(buf)))
if err := l.Init(); err != nil {
t.Fatal(err)
}
type secret struct {
Name string
Passw string `logger:"omit"`
}
s := &secret{Name: "name", Passw: "secret"}
l.Errorf(ctx, "test %#+v", s)
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
}
}

View File

@@ -19,8 +19,6 @@ type Options struct {
Fields []interface{}
// Name holds the logger name
Name string
// Wrappers logger wrapper that called before actual Log/Logf function
Wrappers []Wrapper
// The logging level the logger should log
Level Level
// CallerSkipCount number of frmaes to skip
@@ -83,10 +81,3 @@ func WithName(n string) Option {
o.Name = n
}
}
// WrapLogger adds a logger Wrapper to a list of options passed into the logger
func WrapLogger(w Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}

View File

@@ -8,7 +8,7 @@ import (
"strconv"
"strings"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v4/codec"
)
const sf = "0-+# "
@@ -35,6 +35,7 @@ var (
nilAngleBytes = []byte("<nil>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
filteredBytes = []byte("<filtered>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
@@ -45,25 +46,36 @@ var (
closeMapBytes = []byte("}")
)
type unwrap struct {
val interface{}
s fmt.State
depth int
pointers map[uintptr]int
opts *Options
ignoreNextType bool
type protoMessage interface {
Reset()
ProtoMessage()
}
type Wrapper struct {
val interface{}
s fmt.State
pointers map[uintptr]int
opts *Options
depth int
ignoreNextType bool
takeMap map[int]bool
protoWrapperType bool
sqlWrapperType bool
}
// Options struct
type Options struct {
Codec codec.Codec
Indent string
UnwrapMethods bool
Codec codec.Codec
Indent string
Methods bool
Tagged bool
}
// NewOptions creates new Options struct via provided args
func NewOptions(opts ...Option) Options {
options := Options{
Indent: " ",
UnwrapMethods: false,
Indent: " ",
Methods: false,
}
for _, o := range opts {
o(&options)
@@ -71,34 +83,45 @@ func NewOptions(opts ...Option) Options {
return options
}
// Option func signature
type Option func(*Options)
func UnwrapIndent(f string) Option {
// Indent option specify indent level
func Indent(f string) Option {
return func(o *Options) {
o.Indent = f
}
}
func UnwrapMethods(b bool) Option {
// Methods option toggles fmt.Stringer methods
func Methods(b bool) Option {
return func(o *Options) {
o.UnwrapMethods = b
o.Methods = b
}
}
func UnwrapCodec(c codec.Codec) Option {
// Codec option automatic marshal arg via specified codec and write it to log
func Codec(c codec.Codec) Option {
return func(o *Options) {
o.Codec = c
}
}
func Unwrap(val interface{}, opts ...Option) *unwrap {
options := NewOptions(opts...)
return &unwrap{val: val, opts: &options, pointers: make(map[uintptr]int)}
// Tagged option toggles output only logger:"take" fields
func Tagged(b bool) Option {
return func(o *Options) {
o.Tagged = b
}
}
func (f *unwrap) unpackValue(v reflect.Value) reflect.Value {
func Unwrap(val interface{}, opts ...Option) *Wrapper {
options := NewOptions(opts...)
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeMap: make(map[int]bool)}
}
func (w *Wrapper) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
f.ignoreNextType = false
w.ignoreNextType = false
if !v.IsNil() {
v = v.Elem()
}
@@ -107,19 +130,19 @@ func (f *unwrap) unpackValue(v reflect.Value) reflect.Value {
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
func (f *unwrap) formatPtr(v reflect.Value) {
func (w *Wrapper) formatPtr(v reflect.Value) {
// Display nil if top level pointer is nil.
showTypes := f.s.Flag('#')
if v.IsNil() && (!showTypes || f.ignoreNextType) {
_, _ = f.s.Write(nilAngleBytes)
showTypes := w.s.Flag('#')
if v.IsNil() && (!showTypes || w.ignoreNextType) {
_, _ = w.s.Write(nilAngleBytes)
return
}
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range f.pointers {
if depth >= f.depth {
delete(f.pointers, k)
for k, depth := range w.pointers {
if depth >= w.depth {
delete(w.pointers, k)
}
}
@@ -141,12 +164,12 @@ func (f *unwrap) formatPtr(v reflect.Value) {
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
if pd, ok := w.pointers[addr]; ok && pd < w.depth {
cycleFound = true
indirects--
break
}
f.pointers[addr] = f.depth
w.pointers[addr] = w.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
@@ -159,51 +182,49 @@ func (f *unwrap) formatPtr(v reflect.Value) {
}
// Display type or indirection level depending on flags.
if showTypes && !f.ignoreNextType {
if f.depth > 0 {
_, _ = f.s.Write(openParenBytes)
if showTypes && !w.ignoreNextType {
if w.depth > 0 {
_, _ = w.s.Write(openParenBytes)
}
if f.depth > 0 {
_, _ = f.s.Write(bytes.Repeat(asteriskBytes, indirects))
if w.depth > 0 {
_, _ = w.s.Write(bytes.Repeat(asteriskBytes, indirects))
} else {
_, _ = f.s.Write(bytes.Repeat(ampBytes, indirects))
_, _ = w.s.Write(bytes.Repeat(ampBytes, indirects))
}
_, _ = f.s.Write([]byte(ve.Type().String()))
if f.depth > 0 {
_, _ = f.s.Write(closeParenBytes)
_, _ = w.s.Write([]byte(ve.Type().String()))
if w.depth > 0 {
_, _ = w.s.Write(closeParenBytes)
}
} else {
if nilFound || cycleFound {
indirects += strings.Count(ve.Type().String(), "*")
}
_, _ = f.s.Write(openAngleBytes)
_, _ = f.s.Write([]byte(strings.Repeat("*", indirects)))
_, _ = f.s.Write(closeAngleBytes)
_, _ = w.s.Write(openAngleBytes)
_, _ = w.s.Write([]byte(strings.Repeat("*", indirects)))
_, _ = w.s.Write(closeAngleBytes)
}
// Display pointer information depending on flags.
if f.s.Flag('+') && (len(pointerChain) > 0) {
_, _ = f.s.Write(openParenBytes)
if w.s.Flag('+') && (len(pointerChain) > 0) {
_, _ = w.s.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
_, _ = f.s.Write(pointerChainBytes)
_, _ = w.s.Write(pointerChainBytes)
}
getHexPtr(f.s, addr)
getHexPtr(w.s, addr)
}
_, _ = f.s.Write(closeParenBytes)
_, _ = w.s.Write(closeParenBytes)
}
// Display dereferenced value.
switch {
case nilFound:
_, _ = f.s.Write(nilAngleBytes)
_, _ = w.s.Write(nilAngleBytes)
case cycleFound:
_, _ = f.s.Write(circularShortBytes)
_, _ = w.s.Write(circularShortBytes)
default:
f.ignoreNextType = true
f.format(ve)
w.ignoreNextType = true
w.format(ve)
}
}
@@ -211,54 +232,73 @@ func (f *unwrap) formatPtr(v reflect.Value) {
// uses the passed reflect value to figure out what kind of object we are
// dealing with and formats it appropriately. It is a recursive function,
// however circular data structures are detected and handled properly.
func (f *unwrap) format(v reflect.Value) {
if f.opts.Codec != nil {
buf, err := f.opts.Codec.Marshal(v.Interface())
func (w *Wrapper) format(v reflect.Value) {
if w.opts.Codec != nil {
buf, err := w.opts.Codec.Marshal(v.Interface())
if err != nil {
_, _ = f.s.Write(invalidAngleBytes)
_, _ = w.s.Write(invalidAngleBytes)
return
}
_, _ = f.s.Write(buf)
_, _ = w.s.Write(buf)
return
}
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
_, _ = f.s.Write(invalidAngleBytes)
_, _ = w.s.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
f.formatPtr(v)
switch kind {
case reflect.Ptr:
if !v.IsZero() {
if strings.HasPrefix(reflect.Indirect(v).Type().String(), "wrapperspb.") {
w.protoWrapperType = true
} else if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
w.sqlWrapperType = true
} else if v.CanInterface() {
if _, ok := v.Interface().(protoMessage); ok {
w.protoWrapperType = true
}
}
}
w.formatPtr(v)
return
case reflect.Struct:
if !v.IsZero() {
if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
w.sqlWrapperType = true
}
}
}
// get type information unless already handled elsewhere.
if !f.ignoreNextType && f.s.Flag('#') {
if !w.ignoreNextType && w.s.Flag('#') {
if v.Type().Kind() != reflect.Map &&
v.Type().Kind() != reflect.String &&
v.Type().Kind() != reflect.Array &&
v.Type().Kind() != reflect.Slice {
_, _ = f.s.Write(openParenBytes)
_, _ = w.s.Write(openParenBytes)
}
if v.Kind() != reflect.String {
_, _ = f.s.Write([]byte(v.Type().String()))
_, _ = w.s.Write([]byte(v.Type().String()))
}
if v.Type().Kind() != reflect.Map &&
v.Type().Kind() != reflect.String &&
v.Type().Kind() != reflect.Array &&
v.Type().Kind() != reflect.Slice {
_, _ = f.s.Write(closeParenBytes)
_, _ = w.s.Write(closeParenBytes)
}
}
f.ignoreNextType = false
w.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled.
if !f.opts.UnwrapMethods {
if w.opts.Methods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.opts, f.s, v); handled {
if handled := handleMethods(w.opts, w.s, v); handled {
return
}
}
@@ -266,48 +306,48 @@ func (f *unwrap) format(v reflect.Value) {
switch kind {
case reflect.Invalid:
_, _ = f.s.Write(invalidAngleBytes)
_, _ = w.s.Write(invalidAngleBytes)
case reflect.Bool:
getBool(f.s, v.Bool())
getBool(w.s, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
getInt(f.s, v.Int(), 10)
getInt(w.s, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
getUint(f.s, v.Uint(), 10)
getUint(w.s, v.Uint(), 10)
case reflect.Float32:
getFloat(f.s, v.Float(), 32)
getFloat(w.s, v.Float(), 32)
case reflect.Float64:
getFloat(f.s, v.Float(), 64)
getFloat(w.s, v.Float(), 64)
case reflect.Complex64:
getComplex(f.s, v.Complex(), 32)
getComplex(w.s, v.Complex(), 32)
case reflect.Complex128:
getComplex(f.s, v.Complex(), 64)
getComplex(w.s, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
_, _ = f.s.Write(nilAngleBytes)
_, _ = w.s.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
_, _ = f.s.Write(openBraceBytes)
f.depth++
_, _ = w.s.Write(openBraceBytes)
w.depth++
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
if i > 0 {
_, _ = f.s.Write(commaBytes)
_, _ = f.s.Write(spaceBytes)
_, _ = w.s.Write(commaBytes)
_, _ = w.s.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(v.Index(i)))
w.ignoreNextType = true
w.format(w.unpackValue(v.Index(i)))
}
f.depth--
_, _ = f.s.Write(closeBraceBytes)
w.depth--
_, _ = w.s.Write(closeBraceBytes)
case reflect.String:
_, _ = f.s.Write([]byte(`"` + v.String() + `"`))
_, _ = w.s.Write([]byte(`"` + v.String() + `"`))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
_, _ = f.s.Write(nilAngleBytes)
_, _ = w.s.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
@@ -315,79 +355,123 @@ func (f *unwrap) format(v reflect.Value) {
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
_, _ = f.s.Write(nilAngleBytes)
_, _ = w.s.Write(nilAngleBytes)
break
}
_, _ = f.s.Write(openMapBytes)
f.depth++
_, _ = w.s.Write(openMapBytes)
w.depth++
keys := v.MapKeys()
for i, key := range keys {
if i > 0 {
_, _ = f.s.Write(spaceBytes)
_, _ = w.s.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(key))
_, _ = f.s.Write(colonBytes)
f.ignoreNextType = true
f.format(f.unpackValue(v.MapIndex(key)))
w.ignoreNextType = true
w.format(w.unpackValue(key))
_, _ = w.s.Write(colonBytes)
w.ignoreNextType = true
w.format(w.unpackValue(v.MapIndex(key)))
}
f.depth--
_, _ = f.s.Write(closeMapBytes)
w.depth--
_, _ = w.s.Write(closeMapBytes)
case reflect.Struct:
numFields := v.NumField()
_, _ = f.s.Write(openBraceBytes)
f.depth++
numWritten := 0
_, _ = w.s.Write(openBraceBytes)
w.depth++
vt := v.Type()
prevSkip := false
for i := 0; i < numFields; i++ {
sv, ok := vt.Field(i).Tag.Lookup("logger")
if ok && sv == "omit" {
switch vt.Field(i).Type.PkgPath() {
case "google.golang.org/protobuf/internal/impl", "google.golang.org/protobuf/internal/pragma":
w.protoWrapperType = true
prevSkip = true
continue
}
if i > 0 && !prevSkip {
_, _ = f.s.Write(commaBytes)
_, _ = f.s.Write(spaceBytes)
if w.protoWrapperType && !vt.Field(i).IsExported() {
prevSkip = true
continue
} else if w.sqlWrapperType && vt.Field(i).Name == "Valid" {
prevSkip = true
continue
}
if _, ok := vt.Field(i).Tag.Lookup("protobuf"); ok && !w.protoWrapperType {
w.protoWrapperType = true
}
sv, ok := vt.Field(i).Tag.Lookup("logger")
switch {
case ok:
switch sv {
case "omit":
prevSkip = true
continue
case "take":
break
}
case !ok && w.opts.Tagged:
// skip top level untagged
if w.depth == 1 {
prevSkip = true
continue
}
if tv, ok := w.takeMap[w.depth]; ok && !tv {
prevSkip = true
continue
}
}
if prevSkip {
prevSkip = false
}
vtf := vt.Field(i)
if f.s.Flag('+') || f.s.Flag('#') {
_, _ = f.s.Write([]byte(vtf.Name))
_, _ = f.s.Write(colonBytes)
if numWritten > 0 {
_, _ = w.s.Write(commaBytes)
_, _ = w.s.Write(spaceBytes)
}
f.format(f.unpackValue(v.Field(i)))
vt := vt.Field(i)
if w.s.Flag('+') || w.s.Flag('#') {
_, _ = w.s.Write([]byte(vt.Name))
_, _ = w.s.Write(colonBytes)
}
w.format(w.unpackValue(v.Field(i)))
numWritten++
}
f.depth--
_, _ = f.s.Write(closeBraceBytes)
w.depth--
if numWritten == 0 && w.depth < 0 {
_, _ = w.s.Write(filteredBytes)
}
_, _ = w.s.Write(closeBraceBytes)
case reflect.Uintptr:
getHexPtr(f.s, uintptr(v.Uint()))
getHexPtr(w.s, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
getHexPtr(f.s, v.Pointer())
getHexPtr(w.s, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it if any get added.
default:
format := f.buildDefaultFormat()
format := w.buildDefaultFormat()
if v.CanInterface() {
_, _ = fmt.Fprintf(f.s, format, v.Interface())
_, _ = fmt.Fprintf(w.s, format, v.Interface())
} else {
_, _ = fmt.Fprintf(f.s, format, v.String())
_, _ = fmt.Fprintf(w.s, format, v.String())
}
}
}
func (f *unwrap) Format(s fmt.State, verb rune) {
f.s = s
func (w *Wrapper) Format(s fmt.State, verb rune) {
w.s = s
// Use standard formatting for verbs that are not v.
if verb != 'v' {
format := f.constructOrigFormat(verb)
_, _ = fmt.Fprintf(s, format, f.val)
format := w.constructOrigFormat(verb)
_, _ = fmt.Fprintf(s, format, w.val)
return
}
if f.val == nil {
if w.val == nil {
if s.Flag('#') {
_, _ = s.Write(interfaceBytes)
}
@@ -395,7 +479,11 @@ func (f *unwrap) Format(s fmt.State, verb rune) {
return
}
f.format(reflect.ValueOf(f.val))
if w.opts.Tagged {
w.buildTakeMap(reflect.ValueOf(w.val), 1)
}
w.format(reflect.ValueOf(w.val))
}
// handle special methods like error.Error() or fmt.Stringer interface
@@ -511,11 +599,11 @@ func catchPanic(w io.Writer, _ reflect.Value) {
}
}
func (f *unwrap) buildDefaultFormat() (format string) {
func (w *Wrapper) buildDefaultFormat() (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range sf {
if f.s.Flag(int(flag)) {
if w.s.Flag(int(flag)) {
_, _ = buf.WriteRune(flag)
}
}
@@ -526,26 +614,68 @@ func (f *unwrap) buildDefaultFormat() (format string) {
return format
}
func (f *unwrap) constructOrigFormat(verb rune) (format string) {
func (w *Wrapper) constructOrigFormat(verb rune) string {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range sf {
if f.s.Flag(int(flag)) {
if w.s.Flag(int(flag)) {
_, _ = buf.WriteRune(flag)
}
}
if width, ok := f.s.Width(); ok {
if width, ok := w.s.Width(); ok {
_, _ = buf.WriteString(strconv.Itoa(width))
}
if precision, ok := f.s.Precision(); ok {
if precision, ok := w.s.Precision(); ok {
_, _ = buf.Write(precisionBytes)
_, _ = buf.WriteString(strconv.Itoa(precision))
}
_, _ = buf.WriteRune(verb)
format = buf.String()
return format
return buf.String()
}
func (w *Wrapper) buildTakeMap(v reflect.Value, depth int) {
if !v.IsValid() || v.IsZero() {
return
}
switch v.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
w.buildTakeMap(v.Index(i), depth+1)
}
w.takeMap[depth] = true
return
case reflect.Struct:
break
case reflect.Ptr:
v = v.Elem()
if v.Kind() != reflect.Struct {
w.takeMap[depth] = true
return
}
default:
w.takeMap[depth] = true
return
}
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
sv, ok := vt.Field(i).Tag.Lookup("logger")
if ok && sv == "take" {
w.takeMap[depth] = false
}
if v.Kind() == reflect.Struct ||
(v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
w.buildTakeMap(v.Field(i), depth+1)
}
}
if _, ok := w.takeMap[depth]; !ok {
w.takeMap[depth] = true
}
}

View File

@@ -1,24 +1,13 @@
package unwrap
import (
"fmt"
"strings"
"testing"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v4/codec"
)
func TestUnwrapOmit(t *testing.T) {
type val struct {
MP map[string]string `json:"mp" logger:"omit"`
STR string `json:"str"`
AR []string `json:"ar"`
}
v1 := &val{AR: []string{"string1", "string2"}, STR: "string", MP: map[string]string{"key": "val"}}
t.Logf("output: %#v", v1)
t.Logf("output: %#v", Unwrap(v1))
}
func TestUnwrap(t *testing.T) {
string1 := "string1"
string2 := "string2"
@@ -32,7 +21,10 @@ func TestUnwrap(t *testing.T) {
v1 := &val1{ar: []*string{&string1, &string2}, str: &string1, val: &val1{str: &string2}, mp: map[string]string{"key": "val"}}
t.Logf("output: %#v", Unwrap(v1))
buf := fmt.Sprintf("%#v", Unwrap(v1))
if strings.Compare(buf, `&unwrap.val1{mp:map[string]string{"key":"val"}, val:(*unwrap.val1){mp:map[string]string<nil>, val:(*unwrap.val1)<nil>, str:(*string)"string2", ar:[]*string<nil>}, str:(*string)"string1", ar:[]*string{<*><shown>, <*>"string2"}}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
type val2 struct {
mp map[string]string
@@ -42,11 +34,11 @@ func TestUnwrap(t *testing.T) {
}
v2 := &val2{ar: []string{string1, string2}, str: string1, val: &val2{str: string2}, mp: map[string]string{"key": "val"}}
t.Logf("output: %#v", v2)
_ = v2
// t.Logf("output: %#v", v2)
}
func TestUnwrapCodec(t *testing.T) {
func TestCodec(t *testing.T) {
type val struct {
MP map[string]string `json:"mp"`
STR string `json:"str"`
@@ -55,5 +47,54 @@ func TestUnwrapCodec(t *testing.T) {
v1 := &val{AR: []string{"string1", "string2"}, STR: "string", MP: map[string]string{"key": "val"}}
t.Logf("output: %#v", Unwrap(v1, UnwrapCodec(codec.NewCodec())))
buf := fmt.Sprintf("%#v", Unwrap(v1, Codec(codec.NewCodec())))
if strings.Compare(buf, `{"mp":{"key":"val"},"str":"string","ar":["string1","string2"]}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
}
func TestOmit(t *testing.T) {
type val struct {
Key1 string `logger:"omit"`
Key2 string `logger:"take"`
Key3 string
}
v1 := &val{Key1: "val1", Key2: "val2", Key3: "val3"}
buf := fmt.Sprintf("%#v", Unwrap(v1))
if strings.Compare(buf, `&unwrap.val{Key2:"val2", Key3:"val3"}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
}
func TestTagged(t *testing.T) {
type val struct {
Key1 string `logger:"take"`
Key2 string
}
v1 := &val{Key1: "val1", Key2: "val2"}
buf := fmt.Sprintf("%#v", Unwrap(v1, Tagged(true)))
if strings.Compare(buf, `&unwrap.val{Key1:"val1"}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
}
func TestTaggedNested(t *testing.T) {
type val struct {
key string `logger:"take"`
val string `logger:"omit"`
unk string
}
type str struct {
key string `logger:"omit"`
val *val `logger:"take"`
}
var iface interface{}
v := &str{val: &val{key: "test", unk: "unk"}}
iface = v
buf := fmt.Sprintf("%#v", Unwrap(iface, Tagged(true)))
if strings.Compare(buf, `&unwrap.str{val:(*unwrap.val){key:"test"}}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
}

View File

@@ -1,166 +0,0 @@
package logger // import "go.unistack.org/micro/v3/logger/wrapper"
import (
"context"
"reflect"
rutil "go.unistack.org/micro/v3/util/reflect"
)
// LogFunc function used for Log method
type LogFunc func(ctx context.Context, level Level, args ...interface{})
// LogfFunc function used for Logf method
type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
type Wrapper interface {
// Log logs message with needed level
Log(LogFunc) LogFunc
// Logf logs message with needed level
Logf(LogfFunc) LogfFunc
}
var _ Logger = &omitLogger{}
type omitLogger struct {
l Logger
}
func NewOmitLogger(l Logger) Logger {
return &omitLogger{l: l}
}
func (w *omitLogger) Init(opts ...Option) error {
return w.l.Init(append(opts, WrapLogger(NewOmitWrapper()))...)
}
func (w *omitLogger) V(level Level) bool {
return w.l.V(level)
}
func (w *omitLogger) Level(level Level) {
w.l.Level(level)
}
func (w *omitLogger) Clone(opts ...Option) Logger {
return w.l.Clone(opts...)
}
func (w *omitLogger) Options() Options {
return w.l.Options()
}
func (w *omitLogger) Fields(fields ...interface{}) Logger {
return w.l.Fields(fields...)
}
func (w *omitLogger) Info(ctx context.Context, args ...interface{}) {
w.l.Info(ctx, args...)
}
func (w *omitLogger) Trace(ctx context.Context, args ...interface{}) {
w.l.Trace(ctx, args...)
}
func (w *omitLogger) Debug(ctx context.Context, args ...interface{}) {
w.l.Debug(ctx, args...)
}
func (w *omitLogger) Warn(ctx context.Context, args ...interface{}) {
w.l.Warn(ctx, args...)
}
func (w *omitLogger) Error(ctx context.Context, args ...interface{}) {
w.l.Error(ctx, args...)
}
func (w *omitLogger) Fatal(ctx context.Context, args ...interface{}) {
w.l.Fatal(ctx, args...)
}
func (w *omitLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
w.l.Infof(ctx, msg, args...)
}
func (w *omitLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
w.l.Tracef(ctx, msg, args...)
}
func (w *omitLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
w.l.Debugf(ctx, msg, args...)
}
func (w *omitLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
w.l.Warnf(ctx, msg, args...)
}
func (w *omitLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
w.l.Errorf(ctx, msg, args...)
}
func (w *omitLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
w.l.Fatalf(ctx, msg, args...)
}
func (w *omitLogger) Log(ctx context.Context, level Level, args ...interface{}) {
w.l.Log(ctx, level, args...)
}
func (w *omitLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
w.l.Logf(ctx, level, msg, args...)
}
func (w *omitLogger) String() string {
return w.l.String()
}
type omitWrapper struct{}
func NewOmitWrapper() Wrapper {
return &omitWrapper{}
}
func getArgs(args []interface{}) []interface{} {
nargs := make([]interface{}, 0, len(args))
var err error
for _, arg := range args {
val := reflect.ValueOf(arg)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
narg := arg
if val.Kind() != reflect.Struct {
nargs = append(nargs, narg)
continue
}
if narg, err = rutil.Zero(arg); err != nil {
nargs = append(nargs, narg)
continue
}
rutil.CopyDefaults(narg, arg)
if flds, ferr := rutil.StructFields(narg); ferr == nil {
for _, fld := range flds {
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
fld.Value.Set(reflect.Zero(fld.Value.Type()))
}
}
}
nargs = append(nargs, narg)
}
return nargs
}
func (w *omitWrapper) Log(fn LogFunc) LogFunc {
return func(ctx context.Context, level Level, args ...interface{}) {
fn(ctx, level, getArgs(args)...)
}
}
func (w *omitWrapper) Logf(fn LogfFunc) LogfFunc {
return func(ctx context.Context, level Level, msg string, args ...interface{}) {
fn(ctx, level, msg, getArgs(args)...)
}
}

View File

@@ -5,9 +5,9 @@ import (
"context"
"fmt"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/server"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/server"
)
var (
@@ -66,7 +66,7 @@ var (
}
// DefaultSkipEndpoints wrapper not called for this endpoints
DefaultSkipEndpoints = []string{"Meter.Metrics"}
DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"}
)
type lWrapper struct {
@@ -228,11 +228,7 @@ func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}
for _, o := range l.opts.ClientCallObservers {
labels = append(labels, o(ctx, req, rsp, opts, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return err
}
@@ -255,11 +251,7 @@ func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...clien
for _, o := range l.opts.ClientStreamObservers {
labels = append(labels, o(ctx, req, opts, stream, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return stream, err
}
@@ -282,11 +274,7 @@ func (l *lWrapper) Publish(ctx context.Context, msg client.Message, opts ...clie
for _, o := range l.opts.ClientPublishObservers {
labels = append(labels, o(ctx, msg, opts, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return err
}
@@ -309,11 +297,7 @@ func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp in
for _, o := range l.opts.ServerHandlerObservers {
labels = append(labels, o(ctx, req, rsp, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return err
}
@@ -336,11 +320,7 @@ func (l *lWrapper) ServerSubscriber(ctx context.Context, msg server.Message) err
for _, o := range l.opts.ServerSubscriberObservers {
labels = append(labels, o(ctx, msg, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return err
}
@@ -387,11 +367,7 @@ func (l *lWrapper) ClientCallFunc(ctx context.Context, addr string, req client.R
for _, o := range l.opts.ClientCallFuncObservers {
labels = append(labels, o(ctx, addr, req, rsp, opts, err)...)
}
fields := make(map[string]interface{}, len(labels)/2)
for i := 0; i < len(labels); i += 2 {
fields[labels[i]] = labels[i+1]
}
l.opts.Logger.Fields(fields).Log(ctx, l.opts.Level)
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
return err
}

View File

@@ -39,8 +39,6 @@ func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
// FromContext returns metadata from the given context
// returned metadata shoud not be modified or race condition happens
//
// Deprecated: use FromIncomingContext or FromOutgoingContext
func FromContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
@@ -53,8 +51,6 @@ func FromContext(ctx context.Context) (Metadata, bool) {
}
// NewContext creates a new context with the given metadata
//
// Deprecated: use NewIncomingContext or NewOutgoingContext
func NewContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()

View File

@@ -1,5 +1,5 @@
// Package metadata is a way of defining message headers
package metadata // import "go.unistack.org/micro/v3/metadata"
package metadata // import "go.unistack.org/micro/v4/metadata"
import (
"net/textproto"

View File

@@ -1,12 +0,0 @@
package meter
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
import (
// import required packages
_ "go.unistack.org/micro-proto/v3/api"
// import required packages
_ "go.unistack.org/micro-proto/v3/openapiv3"
)

View File

@@ -1,67 +0,0 @@
package handler // import "go.unistack.org/micro/v3/meter/handler"
import (
"bytes"
"context"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/errors"
"go.unistack.org/micro/v3/meter"
)
// guard to fail early
var _ MeterServer = &Handler{}
type Handler struct {
opts Options
}
type Option func(*Options)
type Options struct {
Meter meter.Meter
Name string
MeterOptions []meter.Option
}
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
func Name(name string) Option {
return func(o *Options) {
o.Name = name
}
}
func MeterOptions(opts ...meter.Option) Option {
return func(o *Options) {
o.MeterOptions = append(o.MeterOptions, opts...)
}
}
func NewOptions(opts ...Option) Options {
options := Options{Meter: meter.DefaultMeter}
for _, o := range opts {
o(&options)
}
return options
}
func NewHandler(opts ...Option) *Handler {
options := NewOptions(opts...)
return &Handler{opts: options}
}
func (h *Handler) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
buf := bytes.NewBuffer(nil)
if err := h.opts.Meter.Write(buf, h.opts.MeterOptions...); err != nil {
return errors.InternalServerError(h.opts.Name, "%v", err)
}
rsp.Data = buf.Bytes()
return nil
}

View File

@@ -1,24 +0,0 @@
syntax = "proto3";
package micro.meter.handler;
option go_package = "go.unistack.org/micro/v3/meter/handler;handler";
import "api/annotations.proto";
import "openapiv3/annotations.proto";
import "codec/frame.proto";
service Meter {
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Metrics";
responses: {
default: {
reference: {
_ref: "micro.codec.Frame";
};
};
};
};
option (micro.api.http) = { get: "/metrics"; };
};
};

View File

@@ -1,32 +0,0 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.5.3
// source: handler.proto
package handler
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
)
var (
MeterName = "Meter"
MeterEndpoints = []api.Endpoint{
{
Name: "Meter.Metrics",
Path: []string{"/metrics"},
Method: []string{"GET"},
Handler: "rpc",
},
}
)
func NewMeterEndpoints() []api.Endpoint {
return MeterEndpoints
}
type MeterServer interface {
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
}

View File

@@ -1,35 +0,0 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.5.3
// source: handler.proto
package handler
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
server "go.unistack.org/micro/v3/server"
)
type meterServer struct {
MeterServer
}
func (h *meterServer) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
return h.MeterServer.Metrics(ctx, req, rsp)
}
func RegisterMeterServer(s server.Server, sh MeterServer, opts ...server.HandlerOption) error {
type meter interface {
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
}
type Meter struct {
meter
}
h := &meterServer{sh}
var nopts []server.HandlerOption
for _, endpoint := range MeterEndpoints {
nopts = append(nopts, api.WithEndpoint(&endpoint))
}
return s.Handle(s.NewHandler(&Meter{h}, append(nopts, opts...)...))
}

View File

@@ -1,5 +1,5 @@
// Package meter is for instrumentation
package meter // import "go.unistack.org/micro/v3/meter"
package meter // import "go.unistack.org/micro/v4/meter"
import (
"io"

View File

@@ -3,7 +3,7 @@ package meter
import (
"context"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v4/logger"
)
// Option powers the configuration for metrics implementations:

View File

@@ -1,56 +1,23 @@
package wrapper // import "go.unistack.org/micro/v3/meter/wrapper"
package wrapper // import "go.unistack.org/micro/v4/meter/wrapper"
import (
"context"
"fmt"
"time"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/server"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/server"
)
var (
// ClientRequestDurationSeconds specifies meter metric name
ClientRequestDurationSeconds = "client_request_duration_seconds"
// ClientRequestLatencyMicroseconds specifies meter metric name
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
// ClientRequestTotal specifies meter metric name
ClientRequestTotal = "client_request_total"
// ClientRequestInflight specifies meter metric name
ClientRequestInflight = "client_request_inflight"
// ServerRequestDurationSeconds specifies meter metric name
ServerRequestDurationSeconds = "server_request_duration_seconds"
// ServerRequestLatencyMicroseconds specifies meter metric name
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
// ServerRequestTotal specifies meter metric name
ServerRequestTotal = "server_request_total"
// ServerRequestInflight specifies meter metric name
ServerRequestInflight = "server_request_inflight"
// PublishMessageDurationSeconds specifies meter metric name
PublishMessageDurationSeconds = "publish_message_duration_seconds"
// PublishMessageLatencyMicroseconds specifies meter metric name
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
// PublishMessageTotal specifies meter metric name
PublishMessageTotal = "publish_message_total"
// PublishMessageInflight specifies meter metric name
PublishMessageInflight = "publish_message_inflight"
// SubscribeMessageDurationSeconds specifies meter metric name
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
// SubscribeMessageLatencyMicroseconds specifies meter metric name
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
// SubscribeMessageTotal specifies meter metric name
SubscribeMessageTotal = "subscribe_message_total"
// SubscribeMessageInflight specifies meter metric name
SubscribeMessageInflight = "subscribe_message_inflight"
labelSuccess = "success"
labelFailure = "failure"
labelStatus = "status"
labelEndpoint = "endpoint"
// DefaultSkipEndpoints contains list of endpoints that not evaluted by wrapper
DefaultSkipEndpoints = []string{"Meter.Metrics"}
DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"}
)
// Options struct
@@ -230,32 +197,11 @@ func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client
}
func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
endpoint := p.Topic()
labels := make([]string, 0, 4)
labels = append(labels, labelEndpoint, endpoint)
w.opts.Meter.Counter(PublishMessageInflight, labels...).Inc()
ts := time.Now()
err := w.Client.Publish(ctx, p, opts...)
te := time.Since(ts)
w.opts.Meter.Counter(PublishMessageInflight, labels...).Dec()
w.opts.Meter.Summary(PublishMessageLatencyMicroseconds, labels...).Update(te.Seconds())
w.opts.Meter.Histogram(PublishMessageDurationSeconds, labels...).Update(te.Seconds())
if err == nil {
labels = append(labels, labelStatus, labelSuccess)
} else {
labels = append(labels, labelStatus, labelFailure)
}
w.opts.Meter.Counter(PublishMessageTotal, labels...).Inc()
return err
return w.Client.Publish(ctx, p, opts...)
}
// NewHandlerWrapper create new server handler wrapper
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
// NewServerHandlerWrapper create new server handler wrapper
func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper {
handler := &wrapper{
opts: NewOptions(opts...),
}
@@ -293,38 +239,3 @@ func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
return err
}
}
// NewSubscriberWrapper create server subscribe wrapper
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
handler := &wrapper{
opts: NewOptions(opts...),
}
return handler.SubscriberFunc
}
func (w *wrapper) SubscriberFunc(fn server.SubscriberFunc) server.SubscriberFunc {
return func(ctx context.Context, msg server.Message) error {
endpoint := msg.Topic()
labels := make([]string, 0, 4)
labels = append(labels, labelEndpoint, endpoint)
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Inc()
ts := time.Now()
err := fn(ctx, msg)
te := time.Since(ts)
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Dec()
w.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, labels...).Update(te.Seconds())
w.opts.Meter.Histogram(SubscribeMessageDurationSeconds, labels...).Update(te.Seconds())
if err == nil {
labels = append(labels, labelStatus, labelSuccess)
} else {
labels = append(labels, labelStatus, labelFailure)
}
w.opts.Meter.Counter(SubscribeMessageTotal, labels...).Inc()
return err
}
}

View File

@@ -1,4 +1,4 @@
package mtls // import "go.unistack.org/micro/v3/mtls"
package mtls // import "go.unistack.org/micro/v4/mtls"
import (
"bytes"

View File

@@ -1,9 +1,9 @@
// Package network is for creating internetworks
package network // import "go.unistack.org/micro/v3/network"
package network // import "go.unistack.org/micro/v4/network"
import (
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/server"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/server"
)
// Error is network node errors

View File

@@ -1,13 +1,13 @@
package network
import (
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/network/tunnel"
"go.unistack.org/micro/v3/proxy"
"go.unistack.org/micro/v3/router"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v3/util/id"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/network/tunnel"
"go.unistack.org/micro/v4/proxy"
"go.unistack.org/micro/v4/router"
"go.unistack.org/micro/v4/tracer"
"go.unistack.org/micro/v4/util/id"
)
// Option func

View File

@@ -8,9 +8,9 @@ import (
"sync"
"time"
maddr "go.unistack.org/micro/v3/util/addr"
mnet "go.unistack.org/micro/v3/util/net"
"go.unistack.org/micro/v3/util/rand"
maddr "go.unistack.org/micro/v4/util/addr"
mnet "go.unistack.org/micro/v4/util/net"
"go.unistack.org/micro/v4/util/rand"
)
type memorySocket struct {

View File

@@ -5,10 +5,10 @@ import (
"crypto/tls"
"time"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/tracer"
)
// Options struct holds the transport options

View File

@@ -1,11 +1,11 @@
// Package transport is an interface for synchronous connection based communication
package transport // import "go.unistack.org/micro/v3/network/transport"
package transport // import "go.unistack.org/micro/v4/network/transport"
import (
"context"
"time"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
var (

View File

@@ -1,15 +1,15 @@
// Package broker is a tunnel broker
package broker // import "go.unistack.org/micro/v3/network/tunnel/broker"
package broker // import "go.unistack.org/micro/v4/network/tunnel/broker"
import (
"context"
"fmt"
"go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/network/tunnel"
"go.unistack.org/micro/v4/broker"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/network/transport"
"go.unistack.org/micro/v4/network/tunnel"
)
type tunBroker struct {

View File

@@ -3,11 +3,11 @@ package tunnel
import (
"time"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v3/util/id"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/network/transport"
"go.unistack.org/micro/v4/tracer"
"go.unistack.org/micro/v4/util/id"
)
var (

View File

@@ -1,8 +1,8 @@
package transport
import (
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/network/tunnel"
"go.unistack.org/micro/v4/network/transport"
"go.unistack.org/micro/v4/network/tunnel"
)
type tunListener struct {

View File

@@ -1,12 +1,12 @@
// Package transport provides a tunnel transport
package transport // import "go.unistack.org/micro/v3/network/tunnel/transport"
package transport // import "go.unistack.org/micro/v4/network/tunnel/transport"
import (
"context"
"fmt"
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/network/tunnel"
"go.unistack.org/micro/v4/network/transport"
"go.unistack.org/micro/v4/network/tunnel"
)
type tunTransport struct {

View File

@@ -1,12 +1,12 @@
// Package tunnel provides gre network tunnelling
package tunnel // import "go.unistack.org/micro/v3/network/transport/tunnel"
package tunnel // import "go.unistack.org/micro/v4/network/transport/tunnel"
import (
"context"
"errors"
"time"
"go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v4/network/transport"
)
// DefaultTunnel contains default tunnel implementation

View File

@@ -5,17 +5,17 @@ import (
"fmt"
"time"
"go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/config"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/register"
"go.unistack.org/micro/v3/router"
"go.unistack.org/micro/v3/server"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/broker"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/config"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/register"
"go.unistack.org/micro/v4/router"
"go.unistack.org/micro/v4/server"
"go.unistack.org/micro/v4/store"
"go.unistack.org/micro/v4/tracer"
)
// Options for micro service
@@ -90,33 +90,7 @@ type Option func(*Options) error
// Broker to be used for client and server
func Broker(b broker.Broker, opts ...BrokerOption) Option {
return func(o *Options) error {
var err error
bopts := brokerOptions{}
for _, opt := range opts {
opt(&bopts)
}
all := false
if len(opts) == 0 {
all = true
}
for _, srv := range o.Servers {
for _, os := range bopts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.Broker(b)); err != nil {
return err
}
}
}
}
for _, cli := range o.Clients {
for _, oc := range bopts.clients {
if cli.Name() == oc || all {
if err = cli.Init(client.Broker(b)); err != nil {
return err
}
}
}
}
o.Brokers = []broker.Broker{b}
return nil
}
}
@@ -211,6 +185,7 @@ func Stores(s ...store.Store) Option {
}
// Logger set the logger to use
//
//nolint:gocyclo
func Logger(l logger.Logger, opts ...LoggerOption) Option {
return func(o *Options) error {
@@ -329,6 +304,7 @@ func Meters(m ...meter.Meter) Option {
// Register sets the register for the service
// and the underlying components
//
//nolint:gocyclo
func Register(r register.Register, opts ...RegisterOption) Option {
return func(o *Options) error {
@@ -403,6 +379,7 @@ func RegisterBroker(n string) RegisterOption {
}
// Tracer sets the tracer
//
//nolint:gocyclo
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
return func(o *Options) error {

31
options/hooks.go Normal file
View File

@@ -0,0 +1,31 @@
package options // import "go.unistack.org/micro/v4/options"
// Hook func interface
type Hook interface{}
// Hooks func slice
type Hooks []Hook
// Append is used to add hooks
func (hs *Hooks) Append(h ...Hook) {
*hs = append(*hs, h...)
}
// Replace is used to set hooks
func (hs *Hooks) Replace(h ...Hook) {
*hs = h
}
// EachNext is used to itearate over hooks forward
func (hs *Hooks) EachNext(fn func(Hook)) {
for idx := 0; idx < len(*hs); idx++ {
fn((*hs)[idx])
}
}
// EachPrev is used to iterate over hooks backward
func (hs *Hooks) EachPrev(fn func(Hook)) {
for idx := len(*hs) - 1; idx >= 0; idx-- {
fn((*hs)[idx])
}
}

65
options/hooks_test.go Normal file
View File

@@ -0,0 +1,65 @@
package options
import "testing"
func TestHooks_Append(t *testing.T) {
fn1 := func() {}
fn2 := func() {}
hs := &Hooks{}
hs.Append(fn1, fn2)
if len(*hs) != 2 {
t.Fatalf("unexpected Append error")
}
}
func TestHooks_Replace(t *testing.T) {
fn1 := func() {}
fn2 := func() {}
hs := &Hooks{}
hs.Append(fn1, fn2, fn1)
if len(*hs) != 3 {
t.Fatalf("unexpected Append error")
}
hs.Replace(fn1, fn2)
if len(*hs) != 2 {
t.Fatalf("unexpected Replace error")
}
}
func TestHooks_EachNext(t *testing.T) {
n := 5
fn1 := func() {
n *= 2
}
fn2 := func() {
n -= 10
}
hs := &Hooks{}
hs.Append(fn1, fn2)
hs.EachNext(func(h Hook) {
h.(func())()
})
if n != 0 {
t.Fatalf("unexpected EachNext")
}
}
func TestHooks_EachPrev(t *testing.T) {
n := 5
fn1 := func() {
n *= 2
}
fn2 := func() {
n -= 10
}
hs := &Hooks{}
hs.Append(fn2, fn1)
hs.EachPrev(func(h Hook) {
h.(func())()
})
if n != 0 {
t.Fatalf("unexpected EachPrev")
}
}

View File

@@ -1,5 +1,5 @@
// Package http enables the http profiler
package http // import "go.unistack.org/micro/v3/profiler/http"
package http // import "go.unistack.org/micro/v4/profiler/http"
import (
"context"
@@ -7,7 +7,7 @@ import (
"net/http/pprof"
"sync"
profile "go.unistack.org/micro/v3/profiler"
profile "go.unistack.org/micro/v4/profiler"
)
type httpProfile struct {

View File

@@ -1,5 +1,5 @@
// Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
package pprof // import "go.unistack.org/micro/v3/profiler/pprof"
package pprof // import "go.unistack.org/micro/v4/profiler/pprof"
import (
"os"
@@ -9,7 +9,7 @@ import (
"sync"
"time"
profile "go.unistack.org/micro/v3/profiler"
profile "go.unistack.org/micro/v4/profiler"
)
type profiler struct {

View File

@@ -1,5 +1,5 @@
// Package profiler is for profilers
package profiler // import "go.unistack.org/micro/v3/profiler"
package profiler // import "go.unistack.org/micro/v4/profiler"
// Profiler interface
type Profiler interface {

View File

@@ -2,11 +2,11 @@
package proxy
import (
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/router"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/router"
"go.unistack.org/micro/v4/tracer"
)
// Options for proxy

View File

@@ -1,10 +1,10 @@
// Package proxy is a transparent proxy built on the micro/server
package proxy // import "go.unistack.org/micro/v3/proxy"
package proxy // import "go.unistack.org/micro/v4/proxy"
import (
"context"
"go.unistack.org/micro/v3/server"
"go.unistack.org/micro/v4/server"
)
// DefaultEndpoint holds default proxy address

View File

@@ -6,7 +6,7 @@ import (
"unicode"
"unicode/utf8"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
// ExtractValue from reflect.Type from specified depth

View File

@@ -5,8 +5,8 @@ import (
"sync"
"time"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/util/id"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/util/id"
)
var (

View File

@@ -3,7 +3,6 @@ package register
import (
"context"
"fmt"
"os"
"sync"
"testing"
"time"
@@ -207,9 +206,9 @@ func TestMemoryRegistryTTLConcurrent(t *testing.T) {
}
}
if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
t.Logf("test will wait %v, then check TTL timeouts", waitTime)
}
//if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
// t.Logf("test will wait %v, then check TTL timeouts", waitTime)
//}
errChan := make(chan error, concurrency)
syncChan := make(chan struct{})

View File

@@ -5,9 +5,9 @@ import (
"crypto/tls"
"time"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/tracer"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/tracer"
)
// Options holds options for register

View File

@@ -1,11 +1,11 @@
// Package register is an interface for service discovery
package register // import "go.unistack.org/micro/v3/register"
package register // import "go.unistack.org/micro/v4/register"
import (
"context"
"errors"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v4/metadata"
)
const (

View File

@@ -1,5 +1,5 @@
// Package dns resolves names to dns records
package dns // import "go.unistack.org/micro/v3/resolver/dns"
package dns // import "go.unistack.org/micro/v4/resolver/dns"
import (
"context"
@@ -7,7 +7,7 @@ import (
"sync"
"time"
"go.unistack.org/micro/v3/resolver"
"go.unistack.org/micro/v4/resolver"
)
// Resolver is a DNS network resolve

View File

@@ -1,11 +1,11 @@
// Package dnssrv resolves names to dns srv records
package dnssrv // import "go.unistack.org/micro/v3/resolver/dnssrv"
package dnssrv // import "go.unistack.org/micro/v4/resolver/dnssrv"
import (
"fmt"
"net"
"go.unistack.org/micro/v3/resolver"
"go.unistack.org/micro/v4/resolver"
)
// Resolver is a DNS network resolve

View File

@@ -1,5 +1,5 @@
// Package http resolves names to network addresses using a http request
package http // import "go.unistack.org/micro/v3/resolver/http"
package http // import "go.unistack.org/micro/v4/resolver/http"
import (
"encoding/json"
@@ -8,7 +8,7 @@ import (
"net/http"
"net/url"
"go.unistack.org/micro/v3/resolver"
"go.unistack.org/micro/v4/resolver"
)
// nolint: golint,revive

Some files were not shown because too many files have changed in this diff Show More