├── .dockerignore ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .swiftformat ├── CODE_OF_CONDUCT.md ├── IntegrationTests ├── allocation-counter-tests-framework │ ├── run-allocation-counter.sh │ └── template │ │ ├── AtomicCounter │ │ ├── Package.swift │ │ └── Sources │ │ │ └── AtomicCounter │ │ │ ├── include │ │ │ └── atomic-counter.h │ │ │ └── src │ │ │ └── atomic-counter.c │ │ ├── HookedFunctionsDoHook │ │ ├── Package.swift │ │ └── Sources │ │ │ └── HookedFunctions │ │ │ ├── include │ │ │ └── hooked-functions.h │ │ │ └── src │ │ │ └── hooked-functions.c │ │ ├── HookedFunctionsDoNotHook │ │ ├── Package.swift │ │ └── Sources │ │ │ └── HookedFunctions │ │ │ ├── include │ │ │ └── hooked-functions.h │ │ │ └── src │ │ │ └── hooked-functions.c │ │ ├── Sources │ │ ├── bootstrapDoHook │ │ │ └── main.c │ │ └── bootstrapDoNotHook │ │ │ └── main.c │ │ └── scaffolding.swift ├── plugin_echo.sh ├── plugin_junit_xml.sh ├── run-single-test.sh ├── run-tests.sh ├── test_functions.sh └── tests_01_performance │ ├── defines.sh │ ├── test_01_allocation_counts.sh │ └── test_01_resources │ ├── README.md │ ├── run-nio-alloc-counter-tests.sh │ ├── shared.swift │ ├── test_001_pass_around_static_strings_small.swift │ └── test_002_pass_around_static_strings_large.swift ├── LICENSE.txt ├── Package.swift ├── README.md ├── Sources ├── Instrumentation │ ├── Instrument.swift │ ├── InstrumentationSystem.swift │ ├── Locks.swift │ ├── MultiplexInstrument.swift │ └── NoOpInstrument.swift ├── NIOInstrumentation │ ├── HTTPHeadersCarrier.swift │ ├── HeaderExtractingHTTPServerHandler.swift │ └── HeaderInjectingHTTPClientHandler.swift ├── OpenTelemetryInstrumentationSupport │ ├── SpanAttribute+EndUser.swift │ ├── SpanAttribute+GRPCSemantics.swift │ ├── SpanAttribute+HTTPSemantics.swift │ ├── SpanAttribute+NetSemantics.swift │ ├── SpanAttribute+PeerSemantics.swift │ ├── SpanAttribute+RPCSemantics.swift │ └── SpanAttributeName.swift ├── Tracing │ ├── InstrumentationSystem+Tracing.swift │ ├── NoOpTracer.swift │ ├── Span.swift │ ├── Timestamp.swift │ └── Tracer.swift ├── TracingBenchmarkTools │ ├── ArgParser.swift │ ├── BenchmarkCategory.swift │ ├── BenchmarkTools.swift │ ├── DriverUtils.swift │ └── README_SWIFT.md └── TracingBenchmarks │ ├── ExampleBenchmark.swift │ └── main.swift ├── Tests ├── InstrumentationTests │ ├── InstrumentTests+XCTest.swift │ ├── InstrumentTests.swift │ ├── InstrumentationSystemTests+XCTest.swift │ └── InstrumentationSystemTests.swift ├── LinuxMain.swift ├── NIOInstrumentationTests │ ├── FakeTracer.swift │ ├── HTTPHeadersCarrierTests+XCTest.swift │ ├── HTTPHeadersCarrierTests.swift │ ├── HTTPHeadersExtractInjectTests+XCTest.swift │ ├── HTTPHeadersExtractInjectTests.swift │ ├── HeaderExtractingHTTPServerHandlerTests+XCTest.swift │ ├── HeaderExtractingHTTPServerHandlerTests.swift │ ├── HeaderInjectingHTTPClientHandlerTests+XCTest.swift │ └── HeaderInjectingHTTPClientHandlerTests.swift ├── OpenTelemetryInstrumentationSupportTests │ ├── SpanAttributeSemanticsTests+XCTest.swift │ └── SpanAttributeSemanticsTests.swift └── TracingTests │ ├── SpanTests+XCTest.swift │ ├── SpanTests.swift │ ├── TestTracer.swift │ ├── TimestampTests+XCTest.swift │ ├── TimestampTests.swift │ ├── TracedLock.swift │ ├── TracedLockTests+XCTest.swift │ ├── TracedLockTests.swift │ ├── TracerTests+XCTest.swift │ ├── TracerTests.swift │ ├── TracingInstrumentationSystemTests+XCTest.swift │ └── TracingInstrumentationSystemTests.swift ├── UseCases ├── .gitignore ├── Package.swift └── Sources │ ├── HTTPEndToEnd │ ├── InstrumentedHTTPClient.swift │ ├── README.md │ ├── Services │ │ ├── OrderServiceHandler.swift │ │ └── StorageServiceHandler.swift │ └── main.swift │ ├── InstrumentsAppTracing │ ├── OSSignpostTracing.swift │ └── main.swift │ ├── InstrumentsAppTracingInstrpkg │ ├── SpansExampleInstrument │ │ ├── SpansExampleInstrument.xcodeproj │ │ │ └── project.pbxproj │ │ └── SpansExampleInstrument │ │ │ ├── SpansExampleInstrument.instrpkg │ │ │ └── SpansExampleInstrumentTemplate.tracetemplate │ └── custom instrument screenshot.png │ ├── ManualAsyncHTTPClient │ └── main.swift │ └── ManualContextPropagation │ └── main.swift ├── dev ├── Dockerfile-5.0.3 ├── Dockerfile-5.1 └── Dockerfile-5.2 ├── images └── zipkin_trace.png └── scripts ├── generate_linux_tests.rb ├── sanity.sh ├── validate_format.sh ├── validate_license_headers.sh └── validate_naming.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .build/ 2 | .swiftpm/ 3 | UseCases/.build/ 4 | UseCases/.swiftpm/ 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | unit-test: 7 | strategy: 8 | matrix: 9 | # GitHub Actions replaces 5.0 with 5 so we have to be specific here 10 | swift: [5.0.3, 5.1, 5.2] 11 | runs-on: ubuntu-latest 12 | container: swift:${{ matrix.swift }} 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v1 16 | - name: Resolve Swift dependencies 17 | run: swift package resolve 18 | - name: Build & Test 19 | run: swift test 20 | compile-usecases: 21 | strategy: 22 | matrix: 23 | # GitHub Actions replaces 5.0 with 5 so we have to be specific here 24 | swift: [5.0.3, 5.1, 5.2] 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v1 29 | - name: Build Docker Image 30 | run: docker build -t swift-tracing:${{ matrix.swift }} -f dev/Dockerfile-${{ matrix.swift }} . 31 | - name: Compile UseCases 32 | run: docker run -v $(pwd):/app --rm -w /app/UseCases --name baggage-context swift-tracing:${{ matrix.swift }} swift build 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm 7 | /Package.resolved 8 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # file options 2 | 3 | --swiftversion 5.0 4 | --exclude .build 5 | --exclude UseCases/.build 6 | --exclude Tests/LinuxMain.swift 7 | --exclude **/*Tests+XCTest.swift 8 | 9 | # format options 10 | 11 | --ifdef no-indent 12 | --patternlet inline 13 | --self insert 14 | --stripunusedargs closure-only 15 | --wraparguments before-first 16 | 17 | # rules 18 | 19 | --disable blankLinesAroundMark 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at swifttracing@slashmo.codes. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/AtomicCounter/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | //===----------------------------------------------------------------------===// 4 | // 5 | // This source file is part of the SwiftNIO open source project 6 | // 7 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 8 | // Licensed under Apache License v2.0 9 | // 10 | // See LICENSE.txt for license information 11 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 12 | // 13 | // SPDX-License-Identifier: Apache-2.0 14 | // 15 | //===----------------------------------------------------------------------===// 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "AtomicCounter", 21 | products: [ 22 | .library(name: "AtomicCounter", targets: ["AtomicCounter"]), 23 | ], 24 | dependencies: [], 25 | targets: [ 26 | .target( 27 | name: "AtomicCounter", 28 | dependencies: [] 29 | ), 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/AtomicCounter/Sources/AtomicCounter/include/atomic-counter.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #ifndef ATOMIC_COUNTER 29 | #define ATOMIC_COUNTER 30 | 31 | #include 32 | 33 | void inc_free_counter(void); 34 | void reset_free_counter(void); 35 | long read_free_counter(void); 36 | 37 | void inc_malloc_counter(void); 38 | void reset_malloc_counter(void); 39 | long read_malloc_counter(void); 40 | 41 | void add_malloc_bytes_counter(intptr_t v); 42 | void reset_malloc_bytes_counter(void); 43 | intptr_t read_malloc_bytes_counter(void); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/AtomicCounter/Sources/AtomicCounter/src/atomic-counter.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #include 29 | 30 | #define MAKE_COUNTER(name) /* 31 | */ _Atomic long g_ ## name ## _counter = ATOMIC_VAR_INIT(0); /* 32 | */ void inc_ ## name ## _counter(void) { /* 33 | */ atomic_fetch_add_explicit(&g_ ## name ## _counter, 1, memory_order_relaxed); /* 34 | */ } /* 35 | */ /* 36 | */ void add_ ## name ## _counter(intptr_t v) { /* 37 | */ atomic_fetch_add_explicit(&g_ ## name ## _counter, v, memory_order_relaxed); /* 38 | */ } /* 39 | */ void reset_ ## name ## _counter(void) { /* 40 | */ atomic_store_explicit(&g_ ## name ## _counter, 0, memory_order_relaxed); /* 41 | */ } /* 42 | */ /* 43 | */ intptr_t read_ ## name ## _counter(void) { /* 44 | */ return atomic_load_explicit(&g_ ## name ## _counter, memory_order_relaxed); /* 45 | */ } 46 | 47 | MAKE_COUNTER(free) 48 | MAKE_COUNTER(malloc) 49 | MAKE_COUNTER(malloc_bytes) 50 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoHook/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | //===----------------------------------------------------------------------===// 4 | // 5 | // This source file is part of the SwiftNIO open source project 6 | // 7 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 8 | // Licensed under Apache License v2.0 9 | // 10 | // See LICENSE.txt for license information 11 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 12 | // 13 | // SPDX-License-Identifier: Apache-2.0 14 | // 15 | //===----------------------------------------------------------------------===// 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "HookedFunctions", 21 | products: [ 22 | .library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"]), 23 | ], 24 | dependencies: [ 25 | .package(url: "../AtomicCounter/", .branch("main")), 26 | ], 27 | targets: [ 28 | .target(name: "HookedFunctions", dependencies: ["AtomicCounter"]), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoHook/Sources/HookedFunctions/include/hooked-functions.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #ifndef HOOKED_FREE 29 | #define HOOKED_FREE 30 | 31 | #include 32 | 33 | void *replacement_malloc(size_t size); 34 | void replacement_free(void *ptr); 35 | void *replacement_calloc(size_t nmemb, size_t size); 36 | void *replacement_realloc(void *ptr, size_t size); 37 | void *replacement_reallocf(void *ptr, size_t size); 38 | void *replacement_valloc(size_t size); 39 | int replacement_posix_memalign(void **memptr, size_t alignment, size_t size); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoHook/Sources/HookedFunctions/src/hooked-functions.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #define _GNU_SOURCE 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | #include 43 | 44 | /* a big block of memory that we'll use for recursive mallocs */ 45 | static char g_recursive_malloc_mem[10 * 1024 * 1024] = {0}; 46 | /* the index of the first free byte */ 47 | static _Atomic ptrdiff_t g_recursive_malloc_next_free_ptr = ATOMIC_VAR_INIT(0); 48 | 49 | #define DYLD_INTERPOSE(_replacement,_replacee) \ 50 | __attribute__((used)) static struct { const void *replacement; const void *replacee; } _interpose_##_replacee \ 51 | __attribute__ ((section("__DATA,__interpose"))) = { (const void *)(unsigned long)&_replacement, (const void *)(unsigned long)&_replacee }; 52 | #define LIBC_SYMBOL(_fun) "" # _fun 53 | 54 | static __thread bool g_in_malloc = false; 55 | static __thread bool g_in_realloc = false; 56 | static __thread bool g_in_free = false; 57 | static __thread void *(*g_libc_malloc)(size_t) = NULL; 58 | static __thread void *(*g_libc_realloc)(void *, size_t) = NULL; 59 | static __thread void (*g_libc_free)(void *) = NULL; 60 | 61 | // this is called if malloc is called whilst trying to resolve libc's realloc. 62 | // we just vend out pointers to a large block in the BSS (which we never free). 63 | // This block should be large enough because it's only used when malloc is 64 | // called from dlsym which should only happen once per thread. 65 | static void *recursive_malloc(size_t size_in) { 66 | size_t size = size_in; 67 | if ((size & 0xf) != 0) { 68 | // make size 16 byte aligned 69 | size = (size + 0xf) & (~(size_t)0xf); 70 | } 71 | 72 | ptrdiff_t next = atomic_fetch_add_explicit(&g_recursive_malloc_next_free_ptr, 73 | size, 74 | memory_order_relaxed); 75 | if ((size_t)next >= sizeof(g_recursive_malloc_mem)) { 76 | // we ran out of memory 77 | return NULL; 78 | } 79 | return (void *)((intptr_t)g_recursive_malloc_mem + next); 80 | } 81 | 82 | static bool is_recursive_malloc_block(void *ptr) { 83 | uintptr_t block_begin = (uintptr_t)g_recursive_malloc_mem; 84 | uintptr_t block_end = block_begin + sizeof(g_recursive_malloc_mem); 85 | uintptr_t user_ptr = (uintptr_t)ptr; 86 | 87 | return user_ptr >= block_begin && user_ptr < block_end; 88 | } 89 | 90 | // this is called if realloc is called whilst trying to resolve libc's realloc. 91 | static void *recursive_realloc(void *ptr, size_t size) { 92 | // not implemented yet... 93 | abort(); 94 | } 95 | 96 | // this is called if free is called whilst trying to resolve libc's free. 97 | static void recursive_free(void *ptr) { 98 | // not implemented yet... 99 | abort(); 100 | } 101 | 102 | #if __APPLE__ 103 | 104 | /* on Darwin calling the original function is super easy, just call it, done. */ 105 | #define JUMP_INTO_LIBC_FUN(_fun, ...) /* \ 106 | */ do { /* \ 107 | */ return _fun(__VA_ARGS__); /* \ 108 | */ } while(0) 109 | 110 | #else 111 | 112 | /* on other UNIX systems this is slightly harder. Basically we see if we already 113 | * have a thread local variable that is a pointer to the original libc function. 114 | * If yes, easy, call it. If no, we need to resolve it which we do by using 115 | * dlsym. There's only one slight problem: dlsym might call back into the 116 | * function we're just trying to resolve (it does call malloc). In that case 117 | * we need to emulate that function (named recursive_*). But that's all then. 118 | */ 119 | #define JUMP_INTO_LIBC_FUN(_fun, ...) /* \ 120 | */ do { /* \ 121 | */ if (!g_libc_ ## _fun) { /* \ 122 | */ if (!g_in_ ## _fun) { /* \ 123 | */ g_in_ ## _fun = true; /* \ 124 | */ g_libc_ ## _fun = dlsym(RTLD_NEXT, LIBC_SYMBOL(_fun)); /* \ 125 | */ } else { /* \ 126 | */ return recursive_ ## _fun (__VA_ARGS__); /* \ 127 | */ } /* \ 128 | */ } /* 129 | */ g_in_ ## _fun = false; /* \ 130 | */ return g_libc_ ## _fun (__VA_ARGS__); /* 131 | */ } while(0) 132 | 133 | #endif 134 | 135 | void replacement_free(void *ptr) { 136 | if (ptr) { 137 | inc_free_counter(); 138 | if (!is_recursive_malloc_block(ptr)) { 139 | JUMP_INTO_LIBC_FUN(free, ptr); 140 | } 141 | } 142 | } 143 | 144 | void *replacement_malloc(size_t size) { 145 | inc_malloc_counter(); 146 | add_malloc_bytes_counter((intptr_t) size); 147 | 148 | JUMP_INTO_LIBC_FUN(malloc, size); 149 | } 150 | 151 | void *replacement_realloc(void *ptr, size_t size) { 152 | if (0 == size) { 153 | replacement_free(ptr); 154 | return NULL; 155 | } 156 | if (!ptr) { 157 | return replacement_malloc(size); 158 | } 159 | inc_free_counter(); 160 | inc_malloc_counter(); 161 | add_malloc_bytes_counter((intptr_t) size); 162 | 163 | JUMP_INTO_LIBC_FUN(realloc, ptr, size); 164 | } 165 | 166 | void *replacement_calloc(size_t count, size_t size) { 167 | void *ptr = replacement_malloc(count * size); 168 | memset(ptr, 0, count * size); 169 | return ptr; 170 | } 171 | 172 | void *replacement_reallocf(void *ptr, size_t size) { 173 | void *new_ptr = replacement_realloc(ptr, size); 174 | if (!new_ptr) { 175 | replacement_free(new_ptr); 176 | } 177 | return new_ptr; 178 | } 179 | 180 | void *replacement_valloc(size_t size) { 181 | // not aligning correctly (should be PAGE_SIZE) but good enough 182 | return replacement_malloc(size); 183 | } 184 | 185 | int replacement_posix_memalign(void **memptr, size_t alignment, size_t size) { 186 | // not aligning correctly (should be `alignment`) but good enough 187 | void *ptr = replacement_malloc(size); 188 | if (ptr && memptr) { 189 | *memptr = ptr; 190 | return 0; 191 | } else { 192 | return 1; 193 | } 194 | } 195 | 196 | #if __APPLE__ 197 | DYLD_INTERPOSE(replacement_free, free) 198 | DYLD_INTERPOSE(replacement_malloc, malloc) 199 | DYLD_INTERPOSE(replacement_realloc, realloc) 200 | DYLD_INTERPOSE(replacement_calloc, calloc) 201 | DYLD_INTERPOSE(replacement_reallocf, reallocf) 202 | DYLD_INTERPOSE(replacement_valloc, valloc) 203 | DYLD_INTERPOSE(replacement_posix_memalign, posix_memalign) 204 | #endif 205 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoNotHook/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | //===----------------------------------------------------------------------===// 4 | // 5 | // This source file is part of the SwiftNIO open source project 6 | // 7 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 8 | // Licensed under Apache License v2.0 9 | // 10 | // See LICENSE.txt for license information 11 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 12 | // 13 | // SPDX-License-Identifier: Apache-2.0 14 | // 15 | //===----------------------------------------------------------------------===// 16 | 17 | import PackageDescription 18 | 19 | let package = Package( 20 | name: "HookedFunctions", 21 | products: [ 22 | .library(name: "HookedFunctions", type: .dynamic, targets: ["HookedFunctions"]), 23 | ], 24 | dependencies: [ 25 | .package(url: "../AtomicCounter/", .branch("main")), 26 | ], 27 | targets: [ 28 | .target(name: "HookedFunctions", dependencies: ["AtomicCounter"]), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoNotHook/Sources/HookedFunctions/include/hooked-functions.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #ifndef HOOKED_FREE 29 | #define HOOKED_FREE 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/HookedFunctionsDoNotHook/Sources/HookedFunctions/src/hooked-functions.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/Sources/bootstrapDoHook/main.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #define _GNU_SOURCE 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #if !__APPLE__ 36 | void free(void *ptr) { 37 | replacement_free(ptr); 38 | } 39 | void *malloc(size_t size) { 40 | return replacement_malloc(size); 41 | } 42 | void *calloc(size_t nmemb, size_t size) { 43 | return replacement_calloc(nmemb, size); 44 | } 45 | void *realloc(void *ptr, size_t size) { 46 | return replacement_realloc(ptr, size); 47 | } 48 | void *reallocf(void *ptr, size_t size) { 49 | return replacement_reallocf(ptr, size); 50 | } 51 | void *valloc(size_t size) { 52 | return replacement_valloc(size); 53 | } 54 | int posix_memalign(void **memptr, size_t alignment, size_t size) { 55 | return replacement_posix_memalign(memptr, alignment, size); 56 | } 57 | #endif 58 | 59 | void swift_main(void); 60 | 61 | int main() { 62 | swift_main(); 63 | } 64 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/Sources/bootstrapDoNotHook/main.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | void swift_main(void); 29 | 30 | int main() { 31 | swift_main(); 32 | } 33 | -------------------------------------------------------------------------------- /IntegrationTests/allocation-counter-tests-framework/template/scaffolding.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | import AtomicCounter 29 | import Foundation 30 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 31 | import Darwin 32 | #else 33 | import Glibc 34 | #endif 35 | 36 | func waitForThreadsToQuiesce(shouldReachZero: Bool) { 37 | func getUnfreed() -> Int { 38 | AtomicCounter.read_malloc_counter() - AtomicCounter.read_free_counter() 39 | } 40 | 41 | var oldNumberOfUnfreed = getUnfreed() 42 | var count = 0 43 | repeat { 44 | guard count < 100 else { 45 | print("WARNING: Giving up, shouldReachZero=\(shouldReachZero), unfreeds=\(oldNumberOfUnfreed)") 46 | return 47 | } 48 | count += 1 49 | usleep(shouldReachZero ? 50000 : 200_000) // allocs/frees happen on multiple threads, allow some cool down time 50 | let newNumberOfUnfreed = getUnfreed() 51 | if oldNumberOfUnfreed == newNumberOfUnfreed, !shouldReachZero || newNumberOfUnfreed <= 0 { 52 | // nothing happened in the last 100ms, let's assume everything's 53 | // calmed down already. 54 | if count > 5 || newNumberOfUnfreed != 0 { 55 | print("DEBUG: After waiting \(count) times, we quiesced to unfreeds=\(newNumberOfUnfreed)") 56 | } 57 | return 58 | } 59 | oldNumberOfUnfreed = newNumberOfUnfreed 60 | } while true 61 | } 62 | 63 | func measureAll(_ fn: () -> Int) -> [[String: Int]] { 64 | func measureOne(throwAway: Bool = false, _ fn: () -> Int) -> [String: Int]? { 65 | AtomicCounter.reset_free_counter() 66 | AtomicCounter.reset_malloc_counter() 67 | AtomicCounter.reset_malloc_bytes_counter() 68 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 69 | autoreleasepool { 70 | _ = fn() 71 | } 72 | #else 73 | _ = fn() 74 | #endif 75 | waitForThreadsToQuiesce(shouldReachZero: !throwAway) 76 | let frees = AtomicCounter.read_free_counter() 77 | let mallocs = AtomicCounter.read_malloc_counter() 78 | let mallocedBytes = AtomicCounter.read_malloc_bytes_counter() 79 | if mallocs - frees < 0 { 80 | print("WARNING: negative remaining allocation count, skipping.") 81 | return nil 82 | } 83 | return [ 84 | "total_allocations": mallocs, 85 | "total_allocated_bytes": mallocedBytes, 86 | "remaining_allocations": mallocs - frees, 87 | ] 88 | } 89 | 90 | _ = measureOne(throwAway: true, fn) /* pre-heat and throw away */ 91 | 92 | var measurements: [[String: Int]] = [] 93 | for _ in 0 ..< 10 { 94 | if let results = measureOne(fn) { 95 | measurements.append(results) 96 | } 97 | } 98 | return measurements 99 | } 100 | 101 | func measureAndPrint(desc: String, fn: () -> Int) { 102 | let measurements = measureAll(fn) 103 | for k in measurements[0].keys { 104 | let vs = measurements.map { $0[k]! } 105 | print("\(desc).\(k): \(vs.min() ?? -1)") 106 | } 107 | print("DEBUG: \(measurements)") 108 | } 109 | 110 | public func measure(identifier: String, _ body: () -> Int) { 111 | measureAndPrint(desc: identifier) { 112 | body() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /IntegrationTests/plugin_echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | function plugin_echo_test_suite_begin() { 30 | echo "Running test suite '$1'" 31 | } 32 | 33 | function plugin_echo_test_suite_end() { 34 | true 35 | } 36 | 37 | # test_name 38 | function plugin_echo_test_begin() { 39 | echo -n "Running test '$1'... " 40 | } 41 | 42 | function plugin_echo_test_skip() { 43 | echo "Skipping test '$1'" 44 | } 45 | 46 | function plugin_echo_test_ok() { 47 | echo "OK (${1}s)" 48 | } 49 | 50 | function plugin_echo_test_fail() { 51 | echo "FAILURE ($1)" 52 | echo "--- OUTPUT BEGIN ---" 53 | cat "$2" 54 | echo "--- OUTPUT END ---" 55 | } 56 | 57 | function plugin_echo_test_end() { 58 | true 59 | } 60 | 61 | function plugin_echo_summary_ok() { 62 | echo "OK (ran $1 tests successfully)" 63 | } 64 | 65 | function plugin_echo_summary_fail() { 66 | echo "FAILURE (oks: $1, failures: $2)" 67 | } 68 | 69 | function plugin_echo_init() { 70 | true 71 | } 72 | -------------------------------------------------------------------------------- /IntegrationTests/plugin_junit_xml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | junit_testsuite_time=0 30 | 31 | function junit_output_write() { 32 | extra_flags="" 33 | if [[ "$1" == "-n" ]]; then 34 | extra_flags="-n" 35 | shift 36 | fi 37 | test -n "$junit_xml_output" 38 | echo $extra_flags "$*" >> "$junit_xml_output" 39 | } 40 | 41 | function junit_output_cat() { 42 | cat "$@" >> "$junit_xml_output" 43 | } 44 | 45 | # search, replace 46 | function junit_output_replace() { 47 | test -n "$junit_xml_output" 48 | case "$(uname -s)" in 49 | Linux) 50 | sed -i "s/$1/$2/g" "$junit_xml_output" 51 | ;; 52 | *) 53 | sed -i "" "s/$1/$2/g" "$junit_xml_output" 54 | ;; 55 | esac 56 | } 57 | 58 | function plugin_junit_xml_test_suite_begin() { 59 | junit_testsuite_time=0 60 | junit_output_write "" 64 | } 65 | 66 | function plugin_junit_xml_test_suite_end() { 67 | junit_repl_success_and_fail "$1" "$2" 68 | junit_output_write "" 69 | } 70 | 71 | # test_name 72 | function plugin_junit_xml_test_begin() { 73 | junit_output_write -n " " 83 | junit_testsuite_time=$((junit_testsuite_time + time_ms)) 84 | } 85 | 86 | function plugin_junit_xml_test_fail() { 87 | time_ms=$1 88 | junit_output_write " time='$time_ms'>" 89 | junit_output_write " " 90 | junit_output_write " " 91 | junit_output_write ' ' 94 | junit_output_write " " 95 | junit_output_write " " 96 | } 97 | 98 | function plugin_junit_xml_test_end() { 99 | junit_output_write " " 100 | } 101 | 102 | function junit_repl_success_and_fail() { 103 | junit_output_replace XXX-TESTS-XXX "$(($1 + $2))" 104 | junit_output_replace XXX-FAILURES-XXX "$2" 105 | junit_output_replace XXX-TIME-XXX "$junit_testsuite_time" 106 | } 107 | 108 | function plugin_junit_xml_summary_ok() { 109 | junit_output_write "" 110 | } 111 | 112 | function plugin_junit_xml_summary_fail() { 113 | junit_output_write "" 114 | } 115 | 116 | function plugin_junit_xml_init() { 117 | junit_xml_output="" 118 | for f in "$@"; do 119 | if [[ "$junit_xml_output" = "PLACEHOLDER" ]]; then 120 | junit_xml_output="$f" 121 | fi 122 | if [[ "$f" == "--junit-xml" && -z "$junit_xml_output" ]]; then 123 | junit_xml_output="PLACEHOLDER" 124 | fi 125 | done 126 | 127 | if [[ -z "$junit_xml_output" || "$junit_xml_output" = "PLACEHOLDER" ]]; then 128 | echo >&2 "ERROR: you need to specify the output after the --junit-xml argument" 129 | false 130 | fi 131 | echo "" > "$junit_xml_output" 132 | } 133 | -------------------------------------------------------------------------------- /IntegrationTests/run-single-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | ( 30 | # this sub-shell is where the actual test is run 31 | set -eu 32 | set -x 33 | set -o pipefail 34 | 35 | test="$1" 36 | tmp="$2" 37 | root="$3" 38 | g_show_info="$4" 39 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 40 | 41 | source "$here/test_functions.sh" 42 | source "$test" 43 | wait 44 | ) 45 | exit_code=$? 46 | exit $exit_code 47 | -------------------------------------------------------------------------------- /IntegrationTests/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | set -eu 30 | 31 | shopt -s nullglob 32 | 33 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 34 | tmp=$(mktemp -d /tmp/.swift-nio-http1-server-sh-tests_XXXXXX) 35 | 36 | # start_time 37 | function time_diff_to_now() { 38 | echo "$(( $(date +%s) - $1 ))" 39 | } 40 | 41 | function plugins_do() { 42 | local method 43 | method="$1" 44 | shift 45 | for plugin in $plugins; do 46 | cd "$orig_cwd" 47 | "plugin_${plugin}_${method}" "$@" 48 | cd - > /dev/null 49 | done 50 | } 51 | 52 | source "$here/plugin_echo.sh" 53 | source "$here/plugin_junit_xml.sh" 54 | 55 | plugins="echo" 56 | plugin_opts_ind=0 57 | if [[ "${1-default}" == "--junit-xml" ]]; then 58 | plugins="echo junit_xml" 59 | plugin_opts_ind=2 60 | fi 61 | 62 | function usage() { 63 | echo >&2 "Usage: $0 [OPTIONS]" 64 | echo >&2 65 | echo >&2 "OPTIONS:" 66 | echo >&2 " -f FILTER: Only run tests matching FILTER (regex)" 67 | } 68 | 69 | orig_cwd=$(pwd) 70 | cd "$here" 71 | 72 | plugins_do init "$@" 73 | shift $plugin_opts_ind 74 | 75 | filter="." 76 | verbose=false 77 | show_info=false 78 | debug=false 79 | while getopts "f:vid" opt; do 80 | case $opt in 81 | f) 82 | filter="$OPTARG" 83 | ;; 84 | v) 85 | verbose=true 86 | ;; 87 | i) 88 | show_info=true 89 | ;; 90 | d) 91 | debug=true 92 | ;; 93 | \?) 94 | usage 95 | exit 1 96 | ;; 97 | esac 98 | done 99 | 100 | function run_test() { 101 | if $verbose; then 102 | "$@" 2>&1 | tee -a "$out" 103 | # we need to return the return value of the first command 104 | return ${PIPESTATUS[0]} 105 | else 106 | "$@" >> "$out" 2>&1 107 | fi 108 | } 109 | 110 | exec 3>&1 4>&2 # copy stdout/err to fd 3/4 to we can output control messages 111 | cnt_ok=0 112 | cnt_fail=0 113 | for f in tests_*; do 114 | suite_ok=0 115 | suite_fail=0 116 | plugins_do test_suite_begin "$f" 117 | start_suite=$(date +%s) 118 | cd "$f" 119 | for t in test_*.sh; do 120 | if [[ ! "$f/$t" =~ $filter ]]; then 121 | plugins_do test_skip "$t" 122 | continue 123 | fi 124 | out=$(mktemp "$tmp/test.out_XXXXXX") 125 | test_tmp=$(mktemp -d "$tmp/test.tmp_XXXXXX") 126 | plugins_do test_begin "$t" "$f" 127 | start=$(date +%s) 128 | if run_test "$here/run-single-test.sh" "$here/$f/$t" "$test_tmp" "$here/.." "$show_info"; then 129 | plugins_do test_ok "$(time_diff_to_now $start)" 130 | suite_ok=$((suite_ok+1)) 131 | if $verbose; then 132 | cat "$out" 133 | fi 134 | else 135 | plugins_do test_fail "$(time_diff_to_now $start)" "$out" 136 | suite_fail=$((suite_fail+1)) 137 | fi 138 | if ! $debug; then 139 | rm "$out" 140 | rm -rf "$test_tmp" 141 | fi 142 | plugins_do test_end 143 | done 144 | cnt_ok=$((cnt_ok + suite_ok)) 145 | cnt_fail=$((cnt_fail + suite_fail)) 146 | cd .. 147 | plugins_do test_suite_end "$(time_diff_to_now $start_suite)" "$suite_ok" "$suite_fail" 148 | done 149 | 150 | if ! $debug; then 151 | rm -rf "$tmp" 152 | else 153 | echo >&2 "debug mode, not deleting '$tmp'" 154 | fi 155 | 156 | 157 | # report 158 | if [[ $cnt_fail > 0 ]]; then 159 | # kill leftovers (the whole process group) 160 | trap '' TERM 161 | kill 0 162 | 163 | plugins_do summary_fail "$cnt_ok" "$cnt_fail" 164 | else 165 | plugins_do summary_ok "$cnt_ok" "$cnt_fail" 166 | fi 167 | 168 | if [[ $cnt_fail > 0 ]]; then 169 | exit 1 170 | else 171 | exit 0 172 | fi 173 | -------------------------------------------------------------------------------- /IntegrationTests/test_functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | function fail() { 29 | echo >&2 "FAILURE: $*" 30 | false 31 | } 32 | 33 | function assert_equal() { 34 | if [[ "$1" != "$2" ]]; then 35 | fail "expected '$1', got '$2' ${3-}" 36 | fi 37 | } 38 | 39 | function assert_equal_files() { 40 | if ! cmp -s "$1" "$2"; then 41 | diff -u "$1" "$2" || true 42 | echo 43 | echo "--- SNIP ($1, size=$(wc "$1"), SHA=$(shasum "$1")) ---" 44 | cat "$1" 45 | echo "--- SNAP ($1)---" 46 | echo "--- SNIP ($2, size=$(wc "$2"), SHA=$(shasum "$2")) ---" 47 | cat "$2" 48 | echo "--- SNAP ($2) ---" 49 | fail "file '$1' not equal to '$2'" 50 | fi 51 | } 52 | 53 | function assert_less_than() { 54 | if [[ ! "$1" -lt "$2" ]]; then 55 | fail "assertion '$1' < '$2' failed" 56 | fi 57 | } 58 | 59 | function assert_less_than_or_equal() { 60 | if [[ ! "$1" -le "$2" ]]; then 61 | fail "assertion '$1' <= '$2' failed" 62 | fi 63 | } 64 | 65 | function assert_greater_than() { 66 | if [[ ! "$1" -gt "$2" ]]; then 67 | fail "assertion '$1' > '$2' failed" 68 | fi 69 | } 70 | 71 | function assert_greater_than_or_equal() { 72 | if [[ ! "$1" -ge "$2" ]]; then 73 | fail "assertion '$1' >= '$2' failed" 74 | fi 75 | } 76 | 77 | g_has_previously_infoed=false 78 | 79 | function info() { 80 | if $g_show_info; then 81 | if ! $g_has_previously_infoed; then 82 | echo >&3 || true # echo an extra newline so it looks better 83 | g_has_previously_infoed=true 84 | fi 85 | echo >&3 "info: $*" || true 86 | fi 87 | } 88 | 89 | function warn() { 90 | echo >&4 "warning: $*" 91 | } 92 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/defines.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | #!/bin/bash 16 | ##===----------------------------------------------------------------------===## 17 | ## 18 | ## This source file is part of the SwiftNIO open source project 19 | ## 20 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 21 | ## Licensed under Apache License v2.0 22 | ## 23 | ## See LICENSE.txt for license information 24 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 25 | ## 26 | ## SPDX-License-Identifier: Apache-2.0 27 | ## 28 | ##===----------------------------------------------------------------------===## 29 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_allocation_counts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | source defines.sh 30 | 31 | set -eu 32 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 33 | 34 | all_tests=() 35 | for file in "$here/test_01_resources/"test_*.swift; do 36 | test_name=$(basename "$file") 37 | test_name=${test_name#test_*} 38 | test_name=${test_name%*.swift} 39 | all_tests+=( "$test_name" ) 40 | done 41 | 42 | "$here/test_01_resources/run-nio-alloc-counter-tests.sh" -t "$tmp" > "$tmp/output" 43 | 44 | for test in "${all_tests[@]}"; do 45 | cat "$tmp/output" # helps debugging 46 | 47 | while read -r test_case; do 48 | test_case=${test_case#test_*} 49 | total_allocations=$(grep "^test_$test_case.total_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g') 50 | not_freed_allocations=$(grep "^test_$test_case.remaining_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g') 51 | max_allowed_env_name="MAX_ALLOCS_ALLOWED_$test_case" 52 | 53 | info "$test_case: allocations not freed: $not_freed_allocations" 54 | info "$test_case: total number of mallocs: $total_allocations" 55 | 56 | assert_less_than "$not_freed_allocations" 5 # allow some slack 57 | assert_greater_than "$not_freed_allocations" -5 # allow some slack 58 | if [[ -z "${!max_allowed_env_name+x}" ]]; then 59 | if [[ -z "${!max_allowed_env_name+x}" ]]; then 60 | warn "no reference number of allocations set (set to \$$max_allowed_env_name)" 61 | warn "to set current number:" 62 | warn " export $max_allowed_env_name=$total_allocations" 63 | fi 64 | else 65 | max_allowed=${!max_allowed_env_name} 66 | assert_less_than_or_equal "$total_allocations" "$max_allowed" 67 | assert_greater_than "$total_allocations" "$(( max_allowed - 1000))" 68 | fi 69 | done < <(grep "^test_$test[^\W]*.total_allocations:" "$tmp/output" | cut -d: -f1 | cut -d. -f1 | sort | uniq) 70 | done 71 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_resources/README.md: -------------------------------------------------------------------------------- 1 | # Allocation Counting Test 2 | 3 | This briefly describes how the allocation counting test works. 4 | 5 | ## How does it work? 6 | 7 | This is possibly the simplest implementation that counts memory allocations (`malloc` and friends) and frees (mostly `free`). It just maintains two atomic variables which count the number of mallocs and the number of frees respectively. We run a simple HTTP1 example -- 1000 requests and responses generated by a simple SwiftNIO based client and server -- and then evaluate the number of mallocs and frees. The difference `mallocs - frees` should be pretty much 0 and the number of `mallocs` should remain stable (or decrease) across commits. We can't establish a perfect baseline as the exact number of allocations depends on your operating system, libc and Swift version. 8 | 9 | ### How are the functions hooked? 10 | 11 | Usually in UNIX it's enough to just define a function, for example 12 | 13 | ```C 14 | void free(void *ptr) { ... } 15 | ``` 16 | 17 | in the main binary and all modules will use this `free` function instead of the real one from the `libc`. For Linux, this is exactly what we're doing, the `bootstrap` binary defines such a `free` function in its `main.c`. On Darwin (macOS/iOS/...) however that is not the case and you need to use [dyld's interpose feature](https://books.google.co.uk/books?id=K8vUkpOXhN4C&lpg=PA73&ots=OMjhRWWwUu&dq=dyld%20interpose&pg=PA73#v=onepage&q=dyld%20interpose&f=false). The odd thing is that dyld's interposing _only_ works if it's in a `.dylib` and not from a binary's main executable. Therefore we need to build a slightly strange SwiftPM package: 18 | 19 | - `bootstrap`: The main executable's main module (written in C) so we can hook the `free` function on Linux. 20 | - `BootstrapSwift`: A SwiftPM module (written in Swift) called in from `bootstrap` which implements the actual SwiftNIO benchmark (and therefore depends on the `NIO` module). 21 | - `HookedFunctions`: A separate SwiftPM package that builds a shared library (`.so` on Linux, `.dylib` on Darwin) which contains the `replacement_malloc`, `replacement_free`, etc functions which just increment an atomic integers representing the number of operations. On Darwin, we use `DYLD_INTERPOSE` in this module, interposing libc functions with our `replacement_` functions. This needs to be a separate SwiftPM package as otherwise its code would just live inside of the `bootstrap` executable and the dyld interposing feature wouldn't work. 22 | - `AtomicCounter`: SwiftPM package (written in C) that implements the atomic counters. It needs to be a separate package as both `BoostrapSwift` (to read the allocation counter) as well as `HookedFree` (to increment the allocation counter) depend on it. 23 | 24 | ## What benchmark is run? 25 | 26 | We run a single TCP connection over which 1000 HTTP requests are made by a client written in NIO, responded to by a server also written in NIO. We re-run the benchmark 10 times and return the lowest number of allocations that has been made. 27 | 28 | ## Why do I have to set a baseline? 29 | 30 | By default this test should always succeed as it doesn't actually compare the number of allocations to a certain number. The reason is that this number varies ever so slightly between operating systems and Swift versions. At the time of writing on macOS we got roughly 326k allocations and on Linux 322k allocations for 1000 HTTP requests & responses. To set a baseline simply run 31 | 32 | ```bash 33 | export MAX_ALLOCS_ALLOWED_1000_reqs_1_conn=327000 34 | ``` 35 | 36 | or similar to set the maximum number of allocations allowed. If the benchmark exceeds these allocations the test will fail. 37 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_resources/run-nio-alloc-counter-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | set -eu 30 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 31 | 32 | tmp_dir="/tmp" 33 | 34 | while getopts "t:" opt; do 35 | case "$opt" in 36 | t) 37 | tmp_dir="$OPTARG" 38 | ;; 39 | *) 40 | exit 1 41 | ;; 42 | esac 43 | done 44 | 45 | shift $((OPTIND-1)) 46 | 47 | tests_to_run=("$here"/test_*.swift) 48 | 49 | if [[ $# -gt 0 ]]; then 50 | tests_to_run=("$@") 51 | fi 52 | 53 | "$here/../../allocation-counter-tests-framework/run-allocation-counter.sh" \ 54 | -p "$here/../../.." \ 55 | -m Baggage \ 56 | -m Instrumentation \ 57 | -m NIOInstrumentation \ 58 | -s "$here/shared.swift" \ 59 | -t "$tmp_dir" \ 60 | "${tests_to_run[@]}" 61 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_resources/shared.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | import Baggage 29 | import Foundation 30 | 31 | @inline(never) 32 | func take1(context: BaggageContext) -> Int { 33 | take2(context: context) 34 | } 35 | 36 | @inline(never) 37 | func take2(context: BaggageContext) -> Int { 38 | take3(context: context) 39 | } 40 | 41 | @inline(never) 42 | func take3(context: BaggageContext) -> Int { 43 | take4(context: context) 44 | } 45 | 46 | @inline(never) 47 | func take4(context: BaggageContext) -> Int { 48 | 42 49 | } 50 | 51 | enum StringKey1: BaggageContextKey { 52 | typealias Value = String 53 | } 54 | 55 | enum StringKey2: BaggageContextKey { 56 | typealias Value = String 57 | } 58 | 59 | enum StringKey3: BaggageContextKey { 60 | typealias Value = String 61 | } 62 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_resources/test_001_pass_around_static_strings_small.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | import Baggage 29 | 30 | func run(identifier: String) { 31 | measure(identifier: identifier) { 32 | var context = BaggageContext() 33 | // static allocated strings 34 | context[StringKey1.self] = "one" 35 | context[StringKey2.self] = "two" 36 | context[StringKey3.self] = "three" 37 | 38 | var numberDone = 1 39 | for _ in 0 ..< 1000 { 40 | let res = take1(context: context) 41 | precondition(res == 42) 42 | numberDone += 1 43 | } 44 | return numberDone 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /IntegrationTests/tests_01_performance/test_01_resources/test_002_pass_around_static_strings_large.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | import Baggage 29 | 30 | func run(identifier: String) { 31 | measure(identifier: identifier) { 32 | var context = BaggageContext() 33 | // static allocated strings 34 | context[StringKey1.self] = String(repeating: "a", count: 30) 35 | context[StringKey2.self] = String(repeating: "b", count: 12) 36 | context[StringKey3.self] = String(repeating: "c", count: 20) 37 | 38 | var numberDone = 1 39 | for _ in 0 ..< 1000 { 40 | let res = take1(context: context) 41 | precondition(res == 42) 42 | numberDone += 1 43 | } 44 | return 1 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "gsoc-swift-tracing", 6 | products: [ 7 | .library(name: "Instrumentation", targets: ["Instrumentation"]), 8 | .library(name: "Tracing", targets: ["Tracing"]), 9 | .library(name: "NIOInstrumentation", targets: ["NIOInstrumentation"]), 10 | .library(name: "OpenTelemetryInstrumentationSupport", targets: ["OpenTelemetryInstrumentationSupport"]), 11 | ], 12 | dependencies: [ 13 | .package( 14 | url: "https://github.com/slashmo/gsoc-swift-baggage-context.git", 15 | from: "0.5.0" 16 | ), 17 | .package(url: "https://github.com/slashmo/swift-nio.git", .branch("feature/baggage-context")), 18 | ], 19 | targets: [ 20 | // ==== -------------------------------------------------------------------------------------------------------- 21 | // MARK: Instrumentation 22 | 23 | .target( 24 | name: "Instrumentation", 25 | dependencies: [ 26 | "Baggage", 27 | "BaggageContext", 28 | ] 29 | ), 30 | .testTarget( 31 | name: "InstrumentationTests", 32 | dependencies: [ 33 | "BaggageContext", 34 | "Instrumentation", 35 | ] 36 | ), 37 | 38 | .target( 39 | name: "Tracing", 40 | dependencies: [ 41 | "BaggageContext", 42 | "Instrumentation", 43 | ] 44 | ), 45 | .testTarget( 46 | name: "TracingTests", 47 | dependencies: [ 48 | "Instrumentation", 49 | "Tracing", 50 | "BaggageContext", 51 | ] 52 | ), 53 | 54 | .target( 55 | name: "NIOInstrumentation", 56 | dependencies: [ 57 | "NIO", 58 | "NIOHTTP1", 59 | "Instrumentation", 60 | ] 61 | ), 62 | .testTarget( 63 | name: "NIOInstrumentationTests", 64 | dependencies: [ 65 | "NIOInstrumentation", 66 | ] 67 | ), 68 | 69 | .target( 70 | name: "OpenTelemetryInstrumentationSupport", 71 | dependencies: [ 72 | .target(name: "Tracing") 73 | ] 74 | ), 75 | .testTarget( 76 | name: "OpenTelemetryInstrumentationSupportTests", 77 | dependencies: [ 78 | "OpenTelemetryInstrumentationSupport", 79 | ] 80 | ), 81 | 82 | // ==== -------------------------------------------------------------------------------------------------------- 83 | // MARK: Performance / Benchmarks 84 | 85 | .target( 86 | name: "TracingBenchmarks", 87 | dependencies: [ 88 | "Baggage", 89 | "TracingBenchmarkTools", 90 | ] 91 | ), 92 | .target( 93 | name: "TracingBenchmarkTools", 94 | dependencies: [] 95 | ), 96 | ] 97 | ) 98 | -------------------------------------------------------------------------------- /Sources/Instrumentation/Instrument.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | 16 | /// Conforming types are used to extract values from a specific `Carrier`. 17 | public protocol ExtractorProtocol { 18 | /// The carrier to extract values from. 19 | associatedtype Carrier 20 | 21 | /// Extract the value for the given key from the `Carrier`. 22 | /// 23 | /// - Parameters: 24 | /// - key: The key to be extracted. 25 | /// - carrier: The `Carrier` to extract from. 26 | func extract(key: String, from carrier: Carrier) -> String? 27 | } 28 | 29 | /// Conforming types are used to inject values into a specific `Carrier`. 30 | public protocol InjectorProtocol { 31 | /// The carrier to inject values into. 32 | associatedtype Carrier 33 | 34 | /// Inject the given value for the given key into the given `Carrier`. 35 | /// 36 | /// - Parameters: 37 | /// - value: The value to be injected. 38 | /// - key: The key for which to inject the value. 39 | /// - carrier: The `Carrier` to inject into. 40 | func inject(_ value: String, forKey key: String, into carrier: inout Carrier) 41 | } 42 | 43 | /// Conforming types are usually cross-cutting tools like tracers. They are agnostic of what specific `Carrier` is used 44 | /// to propagate metadata across boundaries, but instead just specify what values to use for which keys. 45 | public protocol Instrument { 46 | /// Extract values from a `Carrier` by using the given extractor and inject them into the given `BaggageContext`. 47 | /// It's quite common for `Instrument`s to come up with new values if they weren't passed along in the given `Carrier`. 48 | /// 49 | /// - Parameters: 50 | /// - carrier: The `Carrier` that was used to propagate values across boundaries. 51 | /// - baggage: The `Baggage` into which these values should be injected. 52 | /// - extractor: The `Extractor` that extracts values from the given `Carrier`. 53 | func extract( 54 | _ carrier: Carrier, 55 | into baggage: inout Baggage, 56 | using extractor: Extractor 57 | ) 58 | where 59 | Extractor: ExtractorProtocol, 60 | Extractor.Carrier == Carrier 61 | 62 | /// Inject values from a `BaggageContext` and inject them into the given `Carrier` using the given `Injector`. 63 | /// 64 | /// - Parameters: 65 | /// - baggage: The `Baggage` from which relevant information will be extracted. 66 | /// - carrier: The `Carrier` into which this information will be injected. 67 | /// - injector: The `Injector` used to inject extracted `BaggageContext` into the given `Carrier`. 68 | func inject( 69 | _ baggage: Baggage, 70 | into carrier: inout Carrier, 71 | using injector: Injector 72 | ) 73 | where 74 | Injector: InjectorProtocol, 75 | Injector.Carrier == Carrier 76 | } 77 | -------------------------------------------------------------------------------- /Sources/Instrumentation/InstrumentationSystem.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | 16 | /// `InstrumentationSystem` is a global facility where the default cross-cutting tool can be configured. 17 | /// It is set up just once in a given program to select the desired `Instrument` implementation. 18 | /// 19 | /// # Bootstrap multiple Instruments 20 | /// If you need to use more that one cross-cutting tool you can do so by using `MultiplexInstrument`. 21 | /// 22 | /// # Access the Instrument 23 | /// There are two ways of getting the bootstrapped instrument. 24 | /// 1. `InstrumentationSystem.instrument`: Returns whatever you passed to `.bootstrap` as an `Instrument`. 25 | /// 2. `InstrumentationSystem.instrument(of: MyInstrument.self)`: Returns the bootstrapped `Instrument` if it's 26 | /// an instance of the given type or the first instance of `MyInstrument` if it's part of a `MultiplexInstrument`. 27 | /// 28 | /// ## What getter to use 29 | /// - Default to using `InstrumentationSystem.instrument` 30 | /// - Use `InstrumentationSystem.instrument(of: MyInstrument.self)` only if you need to use specific `MyInstrument` APIs 31 | /// 32 | /// Specific instrumentation libraries may also provide their own accessors as extensions, e.g. GreatInstrumentation could provide an 33 | /// `InstrumentationSystem.great` convenience accessor, so prefer using them if available. These accessors should call 34 | /// `.instrument(of: GreatInstrument.self)` under the hood to ensure they work when being used through a `MultiplexInstrument`. 35 | public enum InstrumentationSystem { 36 | private static let lock = ReadWriteLock() 37 | private static var _instrument: Instrument = NoOpInstrument() 38 | private static var isInitialized = false 39 | 40 | /// Globally select the desired `Instrument` implementation. 41 | /// 42 | /// - Parameter instrument: The `Instrument` you want to share globally within your system. 43 | /// - Warning: Do not call this method more than once. This will lead to a crash. 44 | public static func bootstrap(_ instrument: Instrument) { 45 | self.lock.withWriterLock { 46 | precondition( 47 | !self.isInitialized, """ 48 | InstrumentationSystem can only be initialized once per process. Consider using MultiplexInstrument if 49 | you need to use multiple instruments. 50 | """ 51 | ) 52 | self._instrument = instrument 53 | self.isInitialized = true 54 | } 55 | } 56 | 57 | // for our testing we want to allow multiple bootstrapping 58 | internal static func bootstrapInternal(_ instrument: Instrument?) { 59 | self.lock.withWriterLock { 60 | self._instrument = instrument ?? NoOpInstrument() 61 | } 62 | } 63 | 64 | /// Returns the globally configured `Instrument`. 65 | /// 66 | /// Defaults to a no-op `Instrument` if `boostrap` wasn't called before. 67 | public static var instrument: Instrument { 68 | return self.lock.withReaderLock { self._instrument } 69 | } 70 | 71 | /// Get an `Instrument` instance of the given type. 72 | /// 73 | /// When using `MultiplexInstrument`, this returns the first instance of the given type stored in the `MultiplexInstrument`. 74 | /// 75 | /// - Parameter instrumentType: The type of `Instrument` you want to retrieve an instance for. 76 | /// - Returns: An `Instrument` instance of the given type or `nil` if no `Instrument` of that type has been bootstrapped. 77 | public static func instrument(of instrumentType: I.Type) -> I? where I: Instrument { 78 | return self._findInstrument(where: { $0 is I }) as? I 79 | } 80 | } 81 | 82 | extension InstrumentationSystem { 83 | /// :nodoc: INTERNAL API: Do Not Use 84 | public static func _findInstrument(where predicate: (Instrument) -> Bool) -> Instrument? { 85 | return self.lock.withReaderLock { 86 | if let multiplex = self._instrument as? MultiplexInstrument { 87 | return multiplex.firstInstrument(where: predicate) 88 | } else if predicate(self._instrument) { 89 | return self._instrument 90 | } else { 91 | return nil 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sources/Instrumentation/Locks.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // This source file is part of the SwiftNIO open source project 17 | // 18 | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors 19 | // Licensed under Apache License v2.0 20 | // 21 | // See LICENSE.txt for license information 22 | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors 23 | // 24 | // SPDX-License-Identifier: Apache-2.0 25 | // 26 | //===----------------------------------------------------------------------===// 27 | 28 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 29 | import Darwin 30 | #else 31 | import Glibc 32 | #endif 33 | 34 | /// A threading lock based on `libpthread` instead of `libdispatch`. 35 | /// 36 | /// This object provides a lock on top of a single `pthread_mutex_t`. This kind 37 | /// of lock is safe to use with `libpthread`-based threading models, such as the 38 | /// one used by NIO. 39 | internal final class ReadWriteLock { 40 | private let rwlock: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) 41 | 42 | /// Create a new lock. 43 | public init() { 44 | let err = pthread_rwlock_init(self.rwlock, nil) 45 | precondition(err == 0, "pthread_rwlock_init failed with error \(err)") 46 | } 47 | 48 | deinit { 49 | let err = pthread_rwlock_destroy(self.rwlock) 50 | precondition(err == 0, "pthread_rwlock_destroy failed with error \(err)") 51 | self.rwlock.deallocate() 52 | } 53 | 54 | /// Acquire a reader lock. 55 | /// 56 | /// Whenever possible, consider using `withLock` instead of this method and 57 | /// `unlock`, to simplify lock handling. 58 | public func lockRead() { 59 | let err = pthread_rwlock_rdlock(self.rwlock) 60 | precondition(err == 0, "pthread_rwlock_rdlock failed with error \(err)") 61 | } 62 | 63 | /// Acquire a writer lock. 64 | /// 65 | /// Whenever possible, consider using `withLock` instead of this method and 66 | /// `unlock`, to simplify lock handling. 67 | public func lockWrite() { 68 | let err = pthread_rwlock_wrlock(self.rwlock) 69 | precondition(err == 0, "pthread_rwlock_wrlock failed with error \(err)") 70 | } 71 | 72 | /// Release the lock. 73 | /// 74 | /// Whenever possible, consider using `withLock` instead of this method and 75 | /// `lock`, to simplify lock handling. 76 | public func unlock() { 77 | let err = pthread_rwlock_unlock(self.rwlock) 78 | precondition(err == 0, "pthread_rwlock_unlock failed with error \(err)") 79 | } 80 | } 81 | 82 | extension ReadWriteLock { 83 | /// Acquire the reader lock for the duration of the given block. 84 | /// 85 | /// This convenience method should be preferred to `lock` and `unlock` in 86 | /// most situations, as it ensures that the lock will be released regardless 87 | /// of how `body` exits. 88 | /// 89 | /// - Parameter body: The block to execute while holding the lock. 90 | /// - Returns: The value returned by the block. 91 | @inlinable 92 | internal func withReaderLock(_ body: () throws -> T) rethrows -> T { 93 | self.lockRead() 94 | defer { 95 | self.unlock() 96 | } 97 | return try body() 98 | } 99 | 100 | /// Acquire the writer lock for the duration of the given block. 101 | /// 102 | /// This convenience method should be preferred to `lock` and `unlock` in 103 | /// most situations, as it ensures that the lock will be released regardless 104 | /// of how `body` exits. 105 | /// 106 | /// - Parameter body: The block to execute while holding the lock. 107 | /// - Returns: The value returned by the block. 108 | @inlinable 109 | internal func withWriterLock(_ body: () throws -> T) rethrows -> T { 110 | self.lockWrite() 111 | defer { 112 | self.unlock() 113 | } 114 | return try body() 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Sources/Instrumentation/MultiplexInstrument.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | 16 | /// A pseudo-`Instrument` that may be used to instrument using multiple other `Instrument`s across a 17 | /// common `BaggageContext`. 18 | public struct MultiplexInstrument { 19 | private var instruments: [Instrument] 20 | 21 | /// Create a `MultiplexInstrument`. 22 | /// 23 | /// - Parameter instruments: An array of `Instrument`s, each of which will be used to `inject`/`extract` 24 | /// through the same `BaggageContext`. 25 | public init(_ instruments: [Instrument]) { 26 | self.instruments = instruments 27 | } 28 | } 29 | 30 | extension MultiplexInstrument { 31 | func firstInstrument(where predicate: (Instrument) -> Bool) -> Instrument? { 32 | return self.instruments.first(where: predicate) 33 | } 34 | } 35 | 36 | extension MultiplexInstrument: Instrument { 37 | public func inject( 38 | _ baggage: Baggage, into carrier: inout Carrier, using injector: Injector 39 | ) 40 | where 41 | Injector: InjectorProtocol, 42 | Carrier == Injector.Carrier 43 | { 44 | self.instruments.forEach { $0.inject(baggage, into: &carrier, using: injector) } 45 | } 46 | 47 | public func extract( 48 | _ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor 49 | ) 50 | where 51 | Carrier == Extractor.Carrier, 52 | Extractor: ExtractorProtocol 53 | { 54 | self.instruments.forEach { $0.extract(carrier, into: &baggage, using: extractor) } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Instrumentation/NoOpInstrument.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | 16 | /// A "no op" implementation of an `Instrument`. 17 | public struct NoOpInstrument: Instrument { 18 | public init() {} 19 | 20 | public func inject( 21 | _ baggage: Baggage, 22 | into carrier: inout Carrier, 23 | using injector: Injector 24 | ) 25 | where 26 | Injector: InjectorProtocol, 27 | Carrier == Injector.Carrier {} 28 | 29 | public func extract( 30 | _ carrier: Carrier, 31 | into baggage: inout Baggage, 32 | using extractor: Extractor 33 | ) 34 | where 35 | Extractor: ExtractorProtocol, 36 | Carrier == Extractor.Carrier {} 37 | } 38 | -------------------------------------------------------------------------------- /Sources/NIOInstrumentation/HTTPHeadersCarrier.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Instrumentation 15 | import NIOHTTP1 16 | 17 | /// Extracts header values from `NIOHTTP1.HTTPHeaders`. 18 | /// 19 | /// If multiple entries exist for a given key, their values will be joined according to 20 | /// [HTTP RFC 7230: Field Order](https://httpwg.org/specs/rfc7230.html#rfc.section.3.2.2), returning a comma-separated list 21 | /// of the values. 22 | public struct HTTPHeadersExtractor: ExtractorProtocol { 23 | public init() {} 24 | 25 | public func extract(key: String, from headers: HTTPHeaders) -> String? { 26 | let headers = headers 27 | .lazy 28 | .filter { $0.name == key } 29 | .map { $0.value } 30 | return headers.isEmpty ? nil : headers.joined(separator: ",") 31 | } 32 | } 33 | 34 | /// Injects values into `NIOHTTP1.HTTPHeaders`. 35 | public struct HTTPHeadersInjector: InjectorProtocol { 36 | public init() {} 37 | 38 | public func inject(_ value: String, forKey key: String, into headers: inout HTTPHeaders) { 39 | headers.replaceOrAdd(name: key, value: value) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/NIOInstrumentation/HeaderExtractingHTTPServerHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Instrumentation 16 | import NIO 17 | import NIOHTTP1 18 | 19 | import Baggage 20 | import Instrumentation 21 | import NIO 22 | import NIOHTTP1 23 | 24 | public final class HeaderExtractingHTTPServerHandler: ChannelInboundHandler { 25 | public typealias InboundIn = HTTPServerRequestPart 26 | public typealias InboundOut = HTTPServerRequestPart 27 | 28 | public init() {} 29 | 30 | public func channelRead(context: ChannelHandlerContext, data: NIOAny) { 31 | if case .head(let head) = self.unwrapInboundIn(data) { 32 | InstrumentationSystem.instrument.extract( 33 | head.headers, 34 | into: &context.baggage, 35 | using: HTTPHeadersExtractor() 36 | ) 37 | } 38 | 39 | context.fireChannelRead(data) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/NIOInstrumentation/HeaderInjectingHTTPClientHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Instrumentation 16 | import NIO 17 | import NIOHTTP1 18 | 19 | public final class HeaderInjectingHTTPClientHandler: ChannelOutboundHandler { 20 | public typealias OutboundIn = HTTPClientRequestPart 21 | public typealias OutboundOut = HTTPClientRequestPart 22 | 23 | public init() {} 24 | 25 | public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { 26 | guard case .head(var head) = self.unwrapOutboundIn(data) else { 27 | context.write(data, promise: promise) 28 | return 29 | } 30 | 31 | InstrumentationSystem.instrument.inject(context.baggage, into: &head.headers, using: HTTPHeadersInjector()) 32 | context.write(self.wrapOutboundOut(.head(head)), promise: promise) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttribute+EndUser.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | extension SpanAttributeName { 17 | /// - See: EndUserAttributes 18 | public enum EndUser { 19 | /// - See: EndUserAttributes 20 | public static let id = "enduser.id" 21 | /// - See: EndUserAttributes 22 | public static let role = "enduser.role" 23 | /// - See: EndUserAttributes 24 | public static let scope = "enduser.scope" 25 | } 26 | } 27 | 28 | #if swift(>=5.2) 29 | extension SpanAttributes { 30 | /// Semantic end-user attributes. 31 | public var endUser: EndUserAttributes { 32 | get { 33 | .init(attributes: self) 34 | } 35 | set { 36 | self = newValue.attributes 37 | } 38 | } 39 | } 40 | 41 | /// End-user-related semantic conventions as defined in the OpenTelemetry spec. 42 | /// 43 | /// - SeeAlso: [OpenTelemetry: General identity attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md#general-identity-attributes) (as of August 2020) 44 | @dynamicMemberLookup 45 | public struct EndUserAttributes: SpanAttributeNamespace { 46 | public var attributes: SpanAttributes 47 | 48 | public init(attributes: SpanAttributes) { 49 | self.attributes = attributes 50 | } 51 | 52 | public struct NestedAttributes: NestedSpanAttributesProtocol { 53 | public init() {} 54 | 55 | /// Username or client_id extracted from the access token or Authorization header in the inbound request from outside the system. 56 | public var id: SpanAttributeKey { .init(name: SpanAttributeName.EndUser.id) } 57 | 58 | /// Actual/assumed role the client is making the request under extracted from token or application security context. 59 | public var role: SpanAttributeKey { .init(name: SpanAttributeName.EndUser.role) } 60 | 61 | /// Scopes or granted authorities the client currently possesses extracted from token or application security context. 62 | /// The value would come from the scope associated with an OAuth 2.0 Access Token or an attribute value in a SAML 2.0 Assertion. 63 | public var scope: SpanAttributeKey { .init(name: SpanAttributeName.EndUser.scope) } 64 | } 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttribute+GRPCSemantics.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | extension SpanAttributeName { 17 | /// - See: GRPCAttributes 18 | public enum GRPC { 19 | /// - See: GRPCAttributes 20 | public static let messageType = "message.type" 21 | /// - See: GRPCAttributes 22 | public static let messageID = "message.id" 23 | /// - See: GRPCAttributes 24 | public static let messageCompressedSize = "message.compressed_size" 25 | /// - See: GRPCAttributes 26 | public static let messageUncompressedSize = "message.uncompressed_size" 27 | } 28 | } 29 | 30 | #if swift(>=5.2) 31 | extension SpanAttributes { 32 | /// Semantic conventions for gRPC spans. 33 | public var gRPC: GRPCAttributes { 34 | get { 35 | .init(attributes: self) 36 | } 37 | set { 38 | self = newValue.attributes 39 | } 40 | } 41 | } 42 | 43 | /// Semantic conventions for gRPC spans as defined in the OpenTelemetry spec. 44 | /// 45 | /// - SeeAlso: [OpenTelemetry: Semantic conventions for gRPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md#grpc) (as of August 2020) 46 | @dynamicMemberLookup 47 | public struct GRPCAttributes: SpanAttributeNamespace { 48 | public var attributes: SpanAttributes 49 | 50 | public init(attributes: SpanAttributes) { 51 | self.attributes = attributes 52 | } 53 | 54 | public struct NestedAttributes: NestedSpanAttributesProtocol { 55 | public init() {} 56 | 57 | /// The type of message, e.g. "SENT" or "RECEIVED". 58 | public var messageType: SpanAttributeKey { .init(name: SpanAttributeName.GRPC.messageType) } 59 | 60 | /// The message id calculated as two different counters starting from 1, one for sent messages and one for received messages. 61 | public var messageID: SpanAttributeKey { .init(name: SpanAttributeName.GRPC.messageID) } 62 | 63 | /// The compressed message size in bytes. 64 | public var messageCompressedSize: SpanAttributeKey { 65 | .init(name: SpanAttributeName.GRPC.messageCompressedSize) 66 | } 67 | 68 | /// The uncompressed message size in bytes. 69 | public var messageUncompressedSize: SpanAttributeKey { 70 | .init(name: SpanAttributeName.GRPC.messageUncompressedSize) 71 | } 72 | } 73 | } 74 | #endif 75 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttribute+NetSemantics.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | extension SpanAttributeName { 17 | /// - See: NetAttributes 18 | public enum Net { 19 | /// - See: NetAttributes 20 | public static let transport = "net.transport" 21 | /// - See: NetAttributes 22 | public static let peerIP = "net.peer.ip" 23 | /// - See: NetAttributes 24 | public static let peerPort = "net.peer.port" 25 | /// - See: NetAttributes 26 | public static let peerName = "net.peer.name" 27 | /// - See: NetAttributes 28 | public static let hostIP = "net.host.ip" 29 | /// - See: NetAttributes 30 | public static let hostPort = "net.host.port" 31 | /// - See: NetAttributes 32 | public static let hostName = "net.host.name" 33 | } 34 | } 35 | 36 | #if swift(>=5.2) 37 | extension SpanAttributes { 38 | /// Semantic network attributes. 39 | public var net: NetAttributes { 40 | get { 41 | .init(attributes: self) 42 | } 43 | set { 44 | self = newValue.attributes 45 | } 46 | } 47 | } 48 | 49 | /// Network-related semantic conventions as defined in the OpenTelemetry spec. 50 | /// 51 | /// - SeeAlso: [OpenTelemetry: General semantic attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md) (as of August 2020) 52 | @dynamicMemberLookup 53 | public struct NetAttributes: SpanAttributeNamespace { 54 | public var attributes: SpanAttributes 55 | 56 | public init(attributes: SpanAttributes) { 57 | self.attributes = attributes 58 | } 59 | 60 | public struct NestedAttributes: NestedSpanAttributesProtocol { 61 | public init() {} 62 | 63 | /// Transport protocol used. 64 | public var transport: SpanAttributeKey { .init(name: SpanAttributeName.Net.transport) } 65 | 66 | /// Remote address of the peer (dotted decimal for IPv4 or RFC5952 for IPv6). 67 | public var peerIP: SpanAttributeKey { .init(name: SpanAttributeName.Net.peerIP) } 68 | 69 | /// Remote port number as an integer. E.g., 80. 70 | public var peerPort: SpanAttributeKey { .init(name: SpanAttributeName.Net.peerPort) } 71 | 72 | /// Remote hostname or similar. 73 | public var peerName: SpanAttributeKey { .init(name: SpanAttributeName.Net.peerName) } 74 | 75 | /// Like `peerIP` but for the host IP. Useful in case of a multi-IP host. 76 | public var hostIP: SpanAttributeKey { .init(name: SpanAttributeName.Net.hostIP) } 77 | 78 | /// Like `peerPort` but for the host port. 79 | public var hostPort: SpanAttributeKey { .init(name: SpanAttributeName.Net.hostPort) } 80 | 81 | /// Local hostname or similar. 82 | public var hostName: SpanAttributeKey { .init(name: SpanAttributeName.Net.hostName) } 83 | } 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttribute+PeerSemantics.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | extension SpanAttributeName { 17 | /// - See: PeerAttributes 18 | public enum Peer { 19 | /// - See: PeerAttributes 20 | public static let service = "peer.service" 21 | } 22 | } 23 | 24 | #if swift(>=5.2) 25 | extension SpanAttributes { 26 | /// General semantic attributes. 27 | public var peer: PeerAttributes { 28 | get { 29 | .init(attributes: self) 30 | } 31 | set { 32 | self = newValue.attributes 33 | } 34 | } 35 | } 36 | 37 | /// Peer-related semantic conventions as defined in the OpenTelemetry spec. 38 | /// 39 | /// - SeeAlso: [OpenTelemetry: General remote service attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md#general-remote-service-attributes) (as of August 2020) 40 | @dynamicMemberLookup 41 | public struct PeerAttributes: SpanAttributeNamespace { 42 | public var attributes: SpanAttributes 43 | 44 | public init(attributes: SpanAttributes) { 45 | self.attributes = attributes 46 | } 47 | 48 | public struct NestedAttributes: NestedSpanAttributesProtocol { 49 | public init() {} 50 | 51 | /// The service.name of the remote service. SHOULD be equal to the actual service.name resource attribute of the remote service if any. 52 | public var service: SpanAttributeKey { .init(name: SpanAttributeName.Peer.service) } 53 | } 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttribute+RPCSemantics.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | extension SpanAttributeName { 17 | /// - See: RPCAttributes 18 | public enum RPC { 19 | /// - See: RPCAttributes 20 | public static let system = "rpc.system" 21 | /// - See: RPCAttributes 22 | public static let service = "rpc.service" 23 | /// - See: RPCAttributes 24 | public static let method = "rpc.method" 25 | } 26 | } 27 | 28 | #if swift(>=5.2) 29 | extension SpanAttributes { 30 | /// Semantic conventions for RPC spans. 31 | public var rpc: RPCAttributes { 32 | get { 33 | .init(attributes: self) 34 | } 35 | set { 36 | self = newValue.attributes 37 | } 38 | } 39 | } 40 | 41 | /// Semantic conventions for RPC spans as defined in the OpenTelemetry spec. 42 | /// 43 | /// - SeeAlso: [OpenTelemetry: Semantic conventions for RPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/rpc.md) (as of August 2020) 44 | @dynamicMemberLookup 45 | public struct RPCAttributes: SpanAttributeNamespace { 46 | public var attributes: SpanAttributes 47 | 48 | public init(attributes: SpanAttributes) { 49 | self.attributes = attributes 50 | } 51 | 52 | public struct NestedAttributes: NestedSpanAttributesProtocol { 53 | public init() {} 54 | 55 | /// A string identifying the remoting system, e.g., "grpc", "java_rmi" or "wcf". 56 | public var system: SpanAttributeKey { .init(name: SpanAttributeName.RPC.system) } 57 | 58 | /// The full name of the service being called, including its package name, if applicable. 59 | public var service: SpanAttributeKey { .init(name: SpanAttributeName.RPC.service) } 60 | 61 | /// The name of the method being called, must be equal to the $method part in the span name. 62 | public var method: SpanAttributeKey { .init(name: SpanAttributeName.RPC.method) } 63 | } 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /Sources/OpenTelemetryInstrumentationSupport/SpanAttributeName.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Tracing 15 | 16 | /// Namespace for attribute key constants used with `SpanAttributes`. 17 | public enum SpanAttributeName {} 18 | -------------------------------------------------------------------------------- /Sources/Tracing/InstrumentationSystem+Tracing.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Instrumentation 15 | 16 | extension InstrumentationSystem { 17 | /// Get a `Tracer` instance of the given type. 18 | /// 19 | /// When using `MultiplexInstrument`, this returns the first instance of the given type stored in the `MultiplexInstrument`. 20 | /// 21 | /// Usually tracing libraries will provide their own convenience getter, e.g. CoolTracing could provide `InstrumentationSystem.coolTracer`; 22 | /// if available, prefer using those APIs rather than relying on this general function. 23 | /// 24 | /// - Parameter tracerType: The type of `Tracer` you want to retrieve an instance for. 25 | /// - Returns: A `Tracer` instance of the given type or `nil` if no `Tracer` of that type has been bootstrapped. 26 | public static func tracer(of tracerType: T.Type) -> T? where T: Tracer { 27 | return self._findInstrument(where: { $0 is T }) as? T 28 | } 29 | 30 | /// Returns the `Tracer` bootstrapped as part of the `InstrumentationSystem`. 31 | /// 32 | /// If the system was bootstrapped with a `MultiplexInstrument` this function attempts to locate the _first_ 33 | /// tracing instrument as passed to the multiplex instrument. If none is found, a `NoOpTracer` is returned. 34 | /// 35 | /// - Returns: A `Tracer` if the system was bootstrapped with one, and `NoOpTracer` otherwise. 36 | public static var tracer: Tracer { 37 | return (self._findInstrument(where: { $0 is Tracer }) as? Tracer) ?? NoOpTracer() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Tracing/NoOpTracer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Instrumentation 16 | 17 | /// No operation Tracer, used when no tracing is required. 18 | public struct NoOpTracer: Tracer { 19 | public func startSpan( 20 | named operationName: String, 21 | baggage: Baggage, 22 | ofKind kind: SpanKind, 23 | at timestamp: Timestamp 24 | ) -> Span { 25 | return NoOpSpan(baggage: baggage) 26 | } 27 | 28 | public func forceFlush() {} 29 | 30 | public func inject( 31 | _ baggage: Baggage, 32 | into carrier: inout Carrier, 33 | using injector: Injector 34 | ) 35 | where 36 | Injector: InjectorProtocol, 37 | Carrier == Injector.Carrier {} 38 | 39 | public func extract( 40 | _ carrier: Carrier, 41 | into baggage: inout Baggage, 42 | using extractor: Extractor 43 | ) 44 | where 45 | Extractor: ExtractorProtocol, 46 | Carrier == Extractor.Carrier {} 47 | 48 | public final class NoOpSpan: Span { 49 | public let baggage: Baggage 50 | public let isRecording = false 51 | 52 | public init(baggage: Baggage) { 53 | self.baggage = baggage 54 | } 55 | 56 | public func setStatus(_ status: SpanStatus) {} 57 | 58 | public func addLink(_ link: SpanLink) {} 59 | 60 | public func addEvent(_ event: SpanEvent) {} 61 | 62 | public func recordError(_ error: Error) {} 63 | 64 | public var attributes: SpanAttributes { 65 | get { 66 | return [:] 67 | } 68 | set { 69 | // ignore 70 | } 71 | } 72 | 73 | public func end(at timestamp: Timestamp) { 74 | // ignore 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Tracing/Timestamp.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Dispatch 15 | 16 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 17 | import Darwin 18 | #else 19 | import Glibc 20 | #endif 21 | 22 | /// Represents a wall-clock time with microsecond precision. 23 | public struct Timestamp: Comparable, CustomStringConvertible { 24 | private let time: DispatchWallTime 25 | 26 | /// Microseconds since Epoch 27 | public var microsSinceEpoch: Int64 { 28 | return Int64(bitPattern: self.time.rawValue) / -1000 29 | } 30 | 31 | /// Milliseconds since Epoch 32 | public var millisSinceEpoch: Int64 { 33 | return Int64(bitPattern: self.time.rawValue) / -1_000_000 34 | } 35 | 36 | /// Returns the current time. 37 | public static func now() -> Timestamp { 38 | return self.init(time: .now()) 39 | } 40 | 41 | /// A time in the distant future. 42 | public static let distantFuture: Timestamp = .init(time: .distantFuture) 43 | 44 | public init(millisSinceEpoch: Int64) { 45 | let nanoSinceEpoch = UInt64(millisSinceEpoch) * 1_000_000 46 | let seconds = UInt64(nanoSinceEpoch / 1_000_000_000) 47 | let nanoseconds = nanoSinceEpoch - (seconds * 1_000_000_000) 48 | self.init(time: DispatchWallTime(timespec: timespec(tv_sec: Int(seconds), tv_nsec: Int(nanoseconds)))) 49 | } 50 | 51 | public init(time: DispatchWallTime) { 52 | self.time = time 53 | } 54 | 55 | public var description: String { 56 | return "Timestamp(\(self.time.rawValue))" 57 | } 58 | 59 | public static func < (lhs: Timestamp, rhs: Timestamp) -> Bool { 60 | return lhs.time < rhs.time 61 | } 62 | 63 | public static func == (lhs: Timestamp, rhs: Timestamp) -> Bool { 64 | return lhs.time == rhs.time 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Tracing/Tracer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Instrumentation 16 | 17 | /// An `Instrument` with added functionality for distributed tracing. Is uses the span-based tracing model and is 18 | /// based on the OpenTracing/OpenTelemetry spec. 19 | public protocol Tracer: Instrument { 20 | /// Start a new `Span` with the given `Baggage` at a given timestamp. 21 | /// 22 | /// - Parameters: 23 | /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... 24 | /// - baggage: The `Baggage` providing information on where to start the new `Span`. 25 | /// - kind: The `SpanKind` of the new `Span`. 26 | /// - timestamp: The `DispatchTime` at which to start the new `Span`. 27 | func startSpan( 28 | named operationName: String, 29 | baggage: Baggage, 30 | ofKind kind: SpanKind, 31 | at timestamp: Timestamp 32 | ) -> Span 33 | 34 | /// Export all ended spans to the configured backend that have not yet been exported. 35 | /// 36 | /// This function should only be called in cases where it is absolutely necessary, 37 | /// such as when using some FaaS providers that may suspend the process after an invocation, but before the backend exports the completed spans. 38 | /// 39 | /// This function should not block indefinitely, implementations should offer a configurable timeout for flush operations. 40 | func forceFlush() 41 | } 42 | 43 | extension Tracer { 44 | /// Start a new `Span` with the given `Baggage` starting at `Timestamp.now()`. 45 | /// 46 | /// - Parameters: 47 | /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... 48 | /// - context: The carrier of a `BaggageContext` within to start the new `Span`. 49 | /// - kind: The `SpanKind` of the `Span` to be created. Defaults to `.internal`. 50 | public func startSpan( 51 | named operationName: String, 52 | baggage: Baggage, 53 | ofKind kind: SpanKind = .internal 54 | ) -> Span { 55 | return self.startSpan(named: operationName, baggage: baggage, ofKind: kind, at: .now()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/TracingBenchmarkTools/BenchmarkCategory.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | //===----------------------------------------------------------------------===// 15 | // 16 | // Based on: https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils 17 | // 18 | //===----------------------------------------------------------------------===// 19 | 20 | public enum BenchmarkCategory: String { 21 | // Most benchmarks are assumed to be "stable" and will be regularly tracked at 22 | // each commit. A handful may be marked unstable if continually tracking them is 23 | // counterproductive. 24 | case unstable 25 | 26 | // Explicit skip marker 27 | case skip 28 | } 29 | -------------------------------------------------------------------------------- /Sources/TracingBenchmarkTools/README_SWIFT.md: -------------------------------------------------------------------------------- 1 | ## Swift Benchmark Utils 2 | 3 | > This benchmarking infrastructure is copied from 4 | https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils 5 | with the intent of producing similar look and feel, as well as because we need some benchmark infra. 6 | > When feasible we will aim to collaborate and contribute improvements back to the mainline Swift project. 7 | -------------------------------------------------------------------------------- /Sources/TracingBenchmarks/ExampleBenchmark.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import TracingBenchmarkTools 15 | 16 | public let ExampleBenchmarks: [BenchmarkInfo] = [ 17 | BenchmarkInfo( 18 | name: "ExampleBenchmarks.bench_example", 19 | runFunction: { _ in try! bench_example(50000) }, 20 | tags: [], 21 | setUpFunction: { setUp() }, 22 | tearDownFunction: tearDown 23 | ), 24 | ] 25 | 26 | private func setUp() { 27 | // ... 28 | } 29 | 30 | private func tearDown() { 31 | // ... 32 | } 33 | 34 | // completely silly "benchmark" function 35 | func bench_example(_ count: Int) throws { 36 | var sum = 0 37 | for _ in 1 ... count { 38 | sum += 1 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/TracingBenchmarks/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import TracingBenchmarkTools 15 | 16 | assert({ 17 | print("===========================================================================") 18 | print("= !! YOU ARE RUNNING BENCHMARKS IN DEBUG MODE !! =") 19 | print("= When running on the command line, use: `swift run -c release` =") 20 | print("===========================================================================") 21 | return true 22 | }()) 23 | 24 | @inline(__always) 25 | private func registerBenchmark(_ bench: BenchmarkInfo) { 26 | registeredBenchmarks.append(bench) 27 | } 28 | 29 | @inline(__always) 30 | private func registerBenchmark(_ benches: [BenchmarkInfo]) { 31 | benches.forEach(registerBenchmark) 32 | } 33 | 34 | @inline(__always) 35 | private func registerBenchmark(_ name: String, _ function: @escaping (Int) -> Void, _ tags: [BenchmarkCategory]) { 36 | registerBenchmark(BenchmarkInfo(name: name, runFunction: function, tags: tags)) 37 | } 38 | 39 | registerBenchmark(ExampleBenchmarks) 40 | 41 | main() 42 | -------------------------------------------------------------------------------- /Tests/InstrumentationTests/InstrumentTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // InstrumentTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension InstrumentTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (InstrumentTests) -> () throws -> Void)] { 27 | return [ 28 | ("testMultiplexInvokesAllInstruments", testMultiplexInvokesAllInstruments), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/InstrumentationTests/InstrumentTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | import Instrumentation 16 | import XCTest 17 | 18 | final class InstrumentTests: XCTestCase { 19 | func testMultiplexInvokesAllInstruments() { 20 | let instrument = MultiplexInstrument([ 21 | FirstFakeTracer(), 22 | SecondFakeTracer(), 23 | ]) 24 | 25 | var baggage = Baggage.topLevel 26 | instrument.extract([String: String](), into: &baggage, using: DictionaryExtractor()) 27 | 28 | XCTAssertEqual(baggage[FirstFakeTracer.TraceIDKey.self], FirstFakeTracer.defaultTraceID) 29 | XCTAssertEqual(baggage[SecondFakeTracer.TraceIDKey.self], SecondFakeTracer.defaultTraceID) 30 | 31 | var subsequentRequestHeaders = ["Accept": "application/json"] 32 | instrument.inject(baggage, into: &subsequentRequestHeaders, using: DictionaryInjector()) 33 | 34 | XCTAssertEqual(subsequentRequestHeaders, [ 35 | "Accept": "application/json", 36 | FirstFakeTracer.headerName: FirstFakeTracer.defaultTraceID, 37 | SecondFakeTracer.headerName: SecondFakeTracer.defaultTraceID, 38 | ]) 39 | } 40 | } 41 | 42 | private struct DictionaryInjector: InjectorProtocol { 43 | func inject(_ value: String, forKey key: String, into dictionary: inout [String: String]) { 44 | dictionary[key] = value 45 | } 46 | } 47 | 48 | private struct DictionaryExtractor: ExtractorProtocol { 49 | func extract(key: String, from dictionary: [String: String]) -> String? { 50 | return dictionary[key] 51 | } 52 | } 53 | 54 | private final class FirstFakeTracer: Instrument { 55 | enum TraceIDKey: Baggage.Key { 56 | typealias Value = String 57 | 58 | static let name: String? = "FirstFakeTraceID" 59 | } 60 | 61 | static let headerName = "first-fake-trace-id" 62 | static let defaultTraceID = UUID().uuidString 63 | 64 | func inject( 65 | _ baggage: Baggage, into carrier: inout Carrier, using injector: Injector 66 | ) 67 | where 68 | Injector: InjectorProtocol, 69 | Carrier == Injector.Carrier 70 | { 71 | guard let traceID = baggage[TraceIDKey.self] else { return } 72 | injector.inject(traceID, forKey: FirstFakeTracer.headerName, into: &carrier) 73 | } 74 | 75 | func extract( 76 | _ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor 77 | ) 78 | where 79 | Extractor: ExtractorProtocol, 80 | Carrier == Extractor.Carrier 81 | { 82 | let traceID = extractor.extract(key: FirstFakeTracer.headerName, from: carrier) ?? FirstFakeTracer.defaultTraceID 83 | baggage[TraceIDKey.self] = traceID 84 | } 85 | } 86 | 87 | private final class SecondFakeTracer: Instrument { 88 | enum TraceIDKey: Baggage.Key { 89 | typealias Value = String 90 | 91 | static let name: String? = "SecondFakeTraceID" 92 | } 93 | 94 | static let headerName = "second-fake-trace-id" 95 | static let defaultTraceID = UUID().uuidString 96 | 97 | func inject( 98 | _ baggage: Baggage, into carrier: inout Carrier, using injector: Injector 99 | ) 100 | where 101 | Injector: InjectorProtocol, 102 | Carrier == Injector.Carrier 103 | { 104 | guard let traceID = baggage[TraceIDKey.self] else { return } 105 | injector.inject(traceID, forKey: SecondFakeTracer.headerName, into: &carrier) 106 | } 107 | 108 | func extract( 109 | _ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor 110 | ) 111 | where 112 | Extractor: ExtractorProtocol, 113 | Carrier == Extractor.Carrier 114 | { 115 | let traceID = extractor.extract(key: SecondFakeTracer.headerName, from: carrier) ?? SecondFakeTracer.defaultTraceID 116 | baggage[TraceIDKey.self] = traceID 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Tests/InstrumentationTests/InstrumentationSystemTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // InstrumentationSystemTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension InstrumentationSystemTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (InstrumentationSystemTests) -> () throws -> Void)] { 27 | return [ 28 | ("testItProvidesAccessToASingletonInstrument", testItProvidesAccessToASingletonInstrument), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/InstrumentationTests/InstrumentationSystemTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import BaggageContext 16 | @testable import Instrumentation 17 | import XCTest 18 | 19 | final class InstrumentationSystemTests: XCTestCase { 20 | override class func tearDown() { 21 | super.tearDown() 22 | InstrumentationSystem.bootstrapInternal(nil) 23 | } 24 | 25 | func testItProvidesAccessToASingletonInstrument() { 26 | let tracer = FakeTracer() 27 | let instrument = FakeInstrument() 28 | let multiplexInstrument = MultiplexInstrument([tracer, instrument]) 29 | 30 | XCTAssertNil(InstrumentationSystem.instrument(of: FakeTracer.self)) 31 | XCTAssertNil(InstrumentationSystem.instrument(of: FakeInstrument.self)) 32 | 33 | InstrumentationSystem.bootstrapInternal(multiplexInstrument) 34 | XCTAssert(InstrumentationSystem.instrument is MultiplexInstrument) 35 | XCTAssert(InstrumentationSystem.instrument(of: FakeTracer.self) === tracer) 36 | XCTAssert(InstrumentationSystem.instrument(of: FakeInstrument.self) === instrument) 37 | 38 | InstrumentationSystem.bootstrapInternal(tracer) 39 | XCTAssertFalse(InstrumentationSystem.instrument is MultiplexInstrument) 40 | XCTAssert(InstrumentationSystem.instrument(of: FakeTracer.self) === tracer) 41 | XCTAssertNil(InstrumentationSystem.instrument(of: FakeInstrument.self)) 42 | } 43 | } 44 | 45 | private final class FakeTracer: Instrument { 46 | func inject( 47 | _ baggage: Baggage, 48 | into carrier: inout Carrier, 49 | using injector: Injector 50 | ) 51 | where 52 | Injector: InjectorProtocol, 53 | Carrier == Injector.Carrier {} 54 | 55 | func extract( 56 | _ carrier: Carrier, 57 | into baggage: inout Baggage, 58 | using extractor: Extractor 59 | ) 60 | where 61 | Extractor: ExtractorProtocol, 62 | Carrier == Extractor.Carrier {} 63 | } 64 | 65 | private final class FakeInstrument: Instrument { 66 | func inject( 67 | _ baggage: Baggage, 68 | into carrier: inout Carrier, 69 | using injector: Injector 70 | ) 71 | where 72 | Injector: InjectorProtocol, 73 | Carrier == Injector.Carrier {} 74 | 75 | func extract( 76 | _ carrier: Carrier, 77 | into baggage: inout Baggage, 78 | using extractor: Extractor 79 | ) 80 | where 81 | Extractor: ExtractorProtocol, 82 | Carrier == Extractor.Carrier {} 83 | } 84 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // LinuxMain.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | #if os(Linux) || os(FreeBSD) 24 | @testable import InstrumentationTests 25 | @testable import NIOInstrumentationTests 26 | @testable import OpenTelemetryInstrumentationSupportTests 27 | @testable import TracingTests 28 | 29 | // This protocol is necessary to we can call the 'run' method (on an existential of this protocol) 30 | // without the compiler noticing that we're calling a deprecated function. 31 | // This hack exists so we can deprecate individual tests which test deprecated functionality without 32 | // getting a compiler warning... 33 | protocol LinuxMainRunner { func run() } 34 | class LinuxMainRunnerImpl: LinuxMainRunner { 35 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 36 | func run() { 37 | XCTMain([ 38 | testCase(HTTPHeadersCarrierTests.allTests), 39 | testCase(HTTPHeadersExtractInjectTests.allTests), 40 | testCase(HeaderExtractingHTTPServerHandlerTests.allTests), 41 | testCase(HeaderInjectingHTTPClientHandlerTests.allTests), 42 | testCase(InstrumentTests.allTests), 43 | testCase(InstrumentationSystemTests.allTests), 44 | testCase(SpanAttributeSemanticsTests.allTests), 45 | testCase(SpanTests.allTests), 46 | testCase(TimestampTests.allTests), 47 | testCase(TracedLockTests.allTests), 48 | testCase(TracerTests.allTests), 49 | testCase(TracingInstrumentationSystemTests.allTests), 50 | ]) 51 | } 52 | } 53 | (LinuxMainRunnerImpl() as LinuxMainRunner).run() 54 | #endif 55 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/FakeTracer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Foundation 16 | import Instrumentation 17 | import NIOHTTP1 18 | 19 | final class FakeTracer: Instrument { 20 | enum TraceIDKey: Baggage.Key { 21 | typealias Value = String 22 | } 23 | 24 | static let headerName = "fake-trace-id" 25 | static let defaultTraceID = UUID().uuidString 26 | 27 | func inject( 28 | _ baggage: Baggage, into carrier: inout Carrier, using injector: Injector 29 | ) 30 | where 31 | Injector: InjectorProtocol, 32 | Carrier == Injector.Carrier 33 | { 34 | guard let traceID = baggage[TraceIDKey.self] else { return } 35 | injector.inject(traceID, forKey: FakeTracer.headerName, into: &carrier) 36 | } 37 | 38 | func extract( 39 | _ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor 40 | ) 41 | where 42 | Extractor: ExtractorProtocol, 43 | Carrier == Extractor.Carrier 44 | { 45 | let traceID = extractor.extract(key: FakeTracer.headerName, from: carrier) ?? FakeTracer.defaultTraceID 46 | baggage[TraceIDKey.self] = traceID 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HTTPHeadersCarrierTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // HTTPHeadersCarrierTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension HTTPHeadersCarrierTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (HTTPHeadersCarrierTests) -> () throws -> Void)] { 27 | return [ 28 | ("testExtractSingleHeader", testExtractSingleHeader), 29 | ("testExtractNoHeader", testExtractNoHeader), 30 | ("testExtractEmptyHeader", testExtractEmptyHeader), 31 | ("testExtractMultipleHeadersOfSameName", testExtractMultipleHeadersOfSameName), 32 | ] 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HTTPHeadersCarrierTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import NIOHTTP1 15 | import NIOInstrumentation 16 | import XCTest 17 | 18 | final class HTTPHeadersCarrierTests: XCTestCase { 19 | func testExtractSingleHeader() { 20 | let headers: HTTPHeaders = [ 21 | "tracestate": "vendorname1=opaqueValue1,vendorname2=opaqueValue2", 22 | "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", 23 | ] 24 | 25 | let extractor = HTTPHeadersExtractor() 26 | 27 | XCTAssertEqual( 28 | extractor.extract(key: "tracestate", from: headers), 29 | "vendorname1=opaqueValue1,vendorname2=opaqueValue2" 30 | ) 31 | 32 | XCTAssertEqual( 33 | extractor.extract(key: "traceparent", from: headers), 34 | "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01" 35 | ) 36 | } 37 | 38 | func testExtractNoHeader() { 39 | let extractor = HTTPHeadersExtractor() 40 | 41 | XCTAssertNil(extractor.extract(key: "test", from: .init())) 42 | } 43 | 44 | func testExtractEmptyHeader() { 45 | let extractor = HTTPHeadersExtractor() 46 | 47 | XCTAssertEqual(extractor.extract(key: "test", from: ["test": ""]), "") 48 | } 49 | 50 | func testExtractMultipleHeadersOfSameName() { 51 | let headers: HTTPHeaders = [ 52 | "tracestate": "vendorname1=opaqueValue1", 53 | "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", 54 | "tracestate": "vendorname2=opaqueValue2", 55 | ] 56 | 57 | let extractor = HTTPHeadersExtractor() 58 | 59 | XCTAssertEqual( 60 | extractor.extract(key: "tracestate", from: headers), 61 | "vendorname1=opaqueValue1,vendorname2=opaqueValue2" 62 | ) 63 | 64 | XCTAssertEqual( 65 | extractor.extract(key: "traceparent", from: headers), 66 | "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01" 67 | ) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HTTPHeadersExtractInjectTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // HTTPHeadersExtractInjectTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension HTTPHeadersExtractInjectTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (HTTPHeadersExtractInjectTests) -> () throws -> Void)] { 27 | return [ 28 | ("test_extracted_baggage_into_subsequent_request_headers", test_extracted_baggage_into_subsequent_request_headers), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HTTPHeadersExtractInjectTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | @testable import Instrumentation 15 | import NIO 16 | import NIOHTTP1 17 | import NIOInstrumentation 18 | import XCTest 19 | 20 | final class HTTPHeadersExtractInjectTests: XCTestCase { 21 | override class func tearDown() { 22 | super.tearDown() 23 | InstrumentationSystem.bootstrapInternal(nil) 24 | } 25 | 26 | func test_extracted_baggage_into_subsequent_request_headers() throws { 27 | InstrumentationSystem.bootstrapInternal(FakeTracer()) 28 | 29 | let outboundHandler = HeaderInjectingHTTPClientHandler() 30 | let requestHandler = MockRequestHandler() 31 | let inboundHandler = HeaderExtractingHTTPServerHandler() 32 | 33 | let channel = EmbeddedChannel(loop: EmbeddedEventLoop()) 34 | XCTAssertNoThrow(try channel.pipeline.addHandlers([outboundHandler, requestHandler, inboundHandler]).wait()) 35 | 36 | let requestHead = HTTPRequestHead(version: .init(major: 1, minor: 1), method: .GET, uri: "/", headers: [ 37 | FakeTracer.headerName: "abc", 38 | ]) 39 | try channel.writeInbound(HTTPServerRequestPart.head(requestHead)) 40 | 41 | guard case .head(let subsequentRequestHead)? = try channel.readOutbound(as: HTTPClientRequestPart.self) else { 42 | XCTFail("Expected HTTPRequestHead to be written outbound") 43 | return 44 | } 45 | XCTAssertEqual(subsequentRequestHead.headers.count, 2) 46 | XCTAssertEqual(subsequentRequestHead.headers.first(name: "Content-Type"), "application/json") 47 | XCTAssertEqual(subsequentRequestHead.headers.first(name: FakeTracer.headerName), "abc") 48 | } 49 | } 50 | 51 | private final class MockRequestHandler: ChannelDuplexHandler { 52 | typealias InboundIn = HTTPServerRequestPart 53 | typealias OutboundIn = HTTPClientRequestPart 54 | typealias OutboundOut = HTTPClientRequestPart 55 | 56 | func channelReadComplete(context: ChannelHandlerContext) { 57 | let requestHead = HTTPRequestHead(version: .init(major: 1, minor: 1), method: .GET, uri: "/", headers: [ 58 | "Content-Type": "application/json", 59 | ]) 60 | context.writeAndFlush(wrapOutboundOut(.head(requestHead)), promise: nil) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HeaderExtractingHTTPServerHandlerTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // HeaderExtractingHTTPServerHandlerTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension HeaderExtractingHTTPServerHandlerTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (HeaderExtractingHTTPServerHandlerTests) -> () throws -> Void)] { 27 | return [ 28 | ("test_extracts_http_request_headers_into_baggage", test_extracts_http_request_headers_into_baggage), 29 | ("test_respects_previous_baggage_values", test_respects_previous_baggage_values), 30 | ("test_forwards_all_read_events", test_forwards_all_read_events), 31 | ] 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HeaderExtractingHTTPServerHandlerTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | @testable import Instrumentation 16 | import NIO 17 | import NIOHTTP1 18 | import NIOInstrumentation 19 | import XCTest 20 | 21 | final class HeaderExtractingHTTPServerHandlerTests: XCTestCase { 22 | override class func tearDown() { 23 | super.tearDown() 24 | InstrumentationSystem.bootstrapInternal(nil) 25 | } 26 | 27 | func test_extracts_http_request_headers_into_baggage() throws { 28 | InstrumentationSystem.bootstrapInternal(FakeTracer()) 29 | 30 | let traceID = "abc" 31 | let handler = HeaderExtractingHTTPServerHandler() 32 | let channel = EmbeddedChannel(handler: handler, loop: EmbeddedEventLoop()) 33 | 34 | var requestHead = HTTPRequestHead(version: .init(major: 1, minor: 1), method: .GET, uri: "/") 35 | requestHead.headers = [FakeTracer.headerName: traceID] 36 | 37 | XCTAssertNil(channel._channelCore.baggage[FakeTracer.TraceIDKey.self]) 38 | 39 | try channel.writeInbound(HTTPServerRequestPart.head(requestHead)) 40 | 41 | XCTAssertEqual(channel._channelCore.baggage[FakeTracer.TraceIDKey.self], traceID) 42 | } 43 | 44 | func test_respects_previous_baggage_values() throws { 45 | InstrumentationSystem.bootstrapInternal(FakeTracer()) 46 | 47 | let traceID = "abc" 48 | let handler = HeaderExtractingHTTPServerHandler() 49 | let channel = EmbeddedChannel(handler: handler, loop: EmbeddedEventLoop()) 50 | channel._channelCore.baggage[TestKey.self] = "test" 51 | 52 | var requestHead = HTTPRequestHead(version: .init(major: 1, minor: 1), method: .GET, uri: "/") 53 | requestHead.headers = [FakeTracer.headerName: traceID] 54 | 55 | XCTAssertNil(channel._channelCore.baggage[FakeTracer.TraceIDKey.self]) 56 | 57 | try channel.writeInbound(HTTPServerRequestPart.head(requestHead)) 58 | 59 | XCTAssertEqual(channel._channelCore.baggage[FakeTracer.TraceIDKey.self], traceID) 60 | XCTAssertEqual(channel._channelCore.baggage[TestKey.self], "test") 61 | } 62 | 63 | func test_forwards_all_read_events() throws { 64 | let channel = EmbeddedChannel( 65 | handler: HeaderExtractingHTTPServerHandler(), 66 | loop: EmbeddedEventLoop() 67 | ) 68 | 69 | let requestHead = HTTPRequestHead(version: .init(major: 1, minor: 1), method: .GET, uri: "/") 70 | let head = HTTPServerRequestPart.head(requestHead) 71 | try channel.writeInbound(head) 72 | XCTAssertEqual(try channel.readInbound(), head) 73 | 74 | let body = HTTPServerRequestPart.body(channel.allocator.buffer(string: "Test")) 75 | try channel.writeInbound(body) 76 | XCTAssertEqual(try channel.readInbound(), body) 77 | 78 | let end = HTTPServerRequestPart.end(nil) 79 | try channel.writeInbound(end) 80 | XCTAssertEqual(try channel.readInbound(as: HTTPServerRequestPart.self), end) 81 | } 82 | } 83 | 84 | private enum TestKey: Baggage.Key { 85 | typealias Value = String 86 | } 87 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HeaderInjectingHTTPClientHandlerTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // HeaderInjectingHTTPClientHandlerTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension HeaderInjectingHTTPClientHandlerTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (HeaderInjectingHTTPClientHandlerTests) -> () throws -> Void)] { 27 | return [ 28 | ("test_injects_baggage_into_http_request_headers", test_injects_baggage_into_http_request_headers), 29 | ("test_forwards_all_write_events", test_forwards_all_write_events), 30 | ] 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Tests/NIOInstrumentationTests/HeaderInjectingHTTPClientHandlerTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | @testable import Instrumentation 16 | import NIO 17 | import NIOHTTP1 18 | import NIOInstrumentation 19 | import XCTest 20 | 21 | final class HeaderInjectingHTTPClientHandlerTests: XCTestCase { 22 | private let httpVersion = HTTPVersion(major: 1, minor: 1) 23 | 24 | override class func tearDown() { 25 | super.tearDown() 26 | InstrumentationSystem.bootstrapInternal(nil) 27 | } 28 | 29 | func test_injects_baggage_into_http_request_headers() throws { 30 | InstrumentationSystem.bootstrapInternal(FakeTracer()) 31 | 32 | let traceID = "abc" 33 | 34 | var baggage = Baggage.topLevel 35 | baggage[FakeTracer.TraceIDKey.self] = traceID 36 | 37 | let handler = HeaderInjectingHTTPClientHandler() 38 | let loop = EmbeddedEventLoop() 39 | let channel = EmbeddedChannel(handler: handler, loop: loop) 40 | channel._channelCore.baggage = baggage 41 | let requestHead = HTTPRequestHead(version: httpVersion, method: .GET, uri: "/") 42 | 43 | try channel.writeOutbound(HTTPClientRequestPart.head(requestHead)) 44 | 45 | XCTAssertEqual( 46 | try channel.readOutbound(as: HTTPClientRequestPart.self), 47 | .head(.init(version: self.httpVersion, method: .GET, uri: "/", headers: [FakeTracer.headerName: traceID])) 48 | ) 49 | } 50 | 51 | func test_forwards_all_write_events() throws { 52 | let channel = EmbeddedChannel( 53 | handler: HeaderInjectingHTTPClientHandler(), 54 | loop: EmbeddedEventLoop() 55 | ) 56 | 57 | let requestHead = HTTPRequestHead(version: httpVersion, method: .GET, uri: "/") 58 | let head = HTTPClientRequestPart.head(requestHead) 59 | try channel.writeOutbound(head) 60 | XCTAssertEqual(try channel.readOutbound(), head) 61 | 62 | let body = HTTPClientRequestPart.body(.byteBuffer(channel.allocator.buffer(string: "test"))) 63 | try channel.writeOutbound(body) 64 | XCTAssertEqual(try channel.readOutbound(), body) 65 | 66 | let end = HTTPClientRequestPart.end(nil) 67 | try channel.writeOutbound(end) 68 | XCTAssertEqual(try channel.readOutbound(), end) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/OpenTelemetryInstrumentationSupportTests/SpanAttributeSemanticsTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // SpanAttributeSemanticsTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension SpanAttributeSemanticsTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (SpanAttributeSemanticsTests) -> () throws -> Void)] { 27 | return [ 28 | ("testDynamicMemberLookup", testDynamicMemberLookup), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/OpenTelemetryInstrumentationSupportTests/SpanAttributeSemanticsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Instrumentation 16 | import OpenTelemetryInstrumentationSupport 17 | import Tracing 18 | import XCTest 19 | 20 | final class SpanAttributeSemanticsTests: XCTestCase { 21 | func testDynamicMemberLookup() { 22 | #if swift(>=5.2) 23 | var attributes: SpanAttributes = [:] 24 | 25 | attributes.http.method = "GET" 26 | XCTAssertEqual(attributes.http.method, "GET") 27 | 28 | attributes.net.hostPort = 8080 29 | XCTAssertEqual(attributes.net.hostPort, 8080) 30 | 31 | attributes.peer.service = "hotrod" 32 | XCTAssertEqual(attributes.peer.service, "hotrod") 33 | 34 | attributes.endUser.id = "steve" 35 | XCTAssertEqual(attributes.endUser.id, "steve") 36 | #endif 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/TracingTests/SpanTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // SpanTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension SpanTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (SpanTests) -> () throws -> Void)] { 27 | return [ 28 | ("testSpanEventIsExpressibleByStringLiteral", testSpanEventIsExpressibleByStringLiteral), 29 | ("testSpanAttributeIsExpressibleByStringLiteral", testSpanAttributeIsExpressibleByStringLiteral), 30 | ("testSpanAttributeIsExpressibleByStringInterpolation", testSpanAttributeIsExpressibleByStringInterpolation), 31 | ("testSpanAttributeIsExpressibleByIntegerLiteral", testSpanAttributeIsExpressibleByIntegerLiteral), 32 | ("testSpanAttributeIsExpressibleByFloatLiteral", testSpanAttributeIsExpressibleByFloatLiteral), 33 | ("testSpanAttributeIsExpressibleByBooleanLiteral", testSpanAttributeIsExpressibleByBooleanLiteral), 34 | ("testSpanAttributeIsExpressibleByArrayLiteral", testSpanAttributeIsExpressibleByArrayLiteral), 35 | ("testSpanAttributesUX", testSpanAttributesUX), 36 | ("testSpanAttributesCustomValue", testSpanAttributesCustomValue), 37 | ("testSpanAttributesAreIterable", testSpanAttributesAreIterable), 38 | ("testSpanParentConvenience", testSpanParentConvenience), 39 | ] 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Tests/TracingTests/TestTracer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | import Foundation 16 | import Instrumentation 17 | import Tracing 18 | 19 | final class TestTracer: Tracer { 20 | private(set) var spans = [TestSpan]() 21 | 22 | func startSpan( 23 | named operationName: String, 24 | baggage: Baggage, 25 | ofKind kind: SpanKind, 26 | at timestamp: Timestamp 27 | ) -> Span { 28 | let span = TestSpan( 29 | operationName: operationName, 30 | startTimestamp: timestamp, 31 | baggage: baggage, 32 | kind: kind 33 | ) { _ in } 34 | self.spans.append(span) 35 | return span 36 | } 37 | 38 | public func forceFlush() {} 39 | 40 | func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor) 41 | where 42 | Extractor: ExtractorProtocol, 43 | Carrier == Extractor.Carrier 44 | { 45 | let traceID = extractor.extract(key: "trace-id", from: carrier) ?? UUID().uuidString 46 | baggage.traceID = traceID 47 | } 48 | 49 | func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Injector) 50 | where 51 | Injector: InjectorProtocol, 52 | Carrier == Injector.Carrier 53 | { 54 | guard let traceID = baggage.traceID else { return } 55 | injector.inject(traceID, forKey: "trace-id", into: &carrier) 56 | } 57 | } 58 | 59 | extension TestTracer { 60 | enum TraceIDKey: Baggage.Key { 61 | typealias Value = String 62 | } 63 | } 64 | 65 | extension Baggage { 66 | var traceID: String? { 67 | get { 68 | return self[TestTracer.TraceIDKey.self] 69 | } 70 | set { 71 | self[TestTracer.TraceIDKey.self] = newValue 72 | } 73 | } 74 | } 75 | 76 | final class TestSpan: Span { 77 | private let operationName: String 78 | private let kind: SpanKind 79 | 80 | private var status: SpanStatus? 81 | 82 | private let startTimestamp: Timestamp 83 | private(set) var endTimestamp: Timestamp? 84 | 85 | let baggage: Baggage 86 | 87 | private(set) var events = [SpanEvent]() { 88 | didSet { 89 | self.isRecording = !self.events.isEmpty 90 | } 91 | } 92 | 93 | private(set) var links = [SpanLink]() 94 | 95 | var attributes: SpanAttributes = [:] { 96 | didSet { 97 | self.isRecording = !self.attributes.isEmpty 98 | } 99 | } 100 | 101 | private(set) var isRecording = false 102 | 103 | let onEnd: (Span) -> Void 104 | 105 | init( 106 | operationName: String, 107 | startTimestamp: Timestamp, 108 | baggage: Baggage, 109 | kind: SpanKind, 110 | onEnd: @escaping (Span) -> Void 111 | ) { 112 | self.operationName = operationName 113 | self.startTimestamp = startTimestamp 114 | self.baggage = baggage 115 | self.onEnd = onEnd 116 | self.kind = kind 117 | } 118 | 119 | func setStatus(_ status: SpanStatus) { 120 | self.status = status 121 | self.isRecording = true 122 | } 123 | 124 | func addLink(_ link: SpanLink) { 125 | self.links.append(link) 126 | } 127 | 128 | func addEvent(_ event: SpanEvent) { 129 | self.events.append(event) 130 | } 131 | 132 | func recordError(_ error: Error) {} 133 | 134 | func end(at timestamp: Timestamp) { 135 | self.endTimestamp = timestamp 136 | self.onEnd(self) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Tests/TracingTests/TimestampTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // TimestampTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension TimestampTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (TimestampTests) -> () throws -> Void)] { 27 | return [ 28 | ("test_timestamp_now", test_timestamp_now), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/TracingTests/TimestampTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Instrumentation 15 | import Tracing 16 | import XCTest 17 | 18 | final class TimestampTests: XCTestCase { 19 | func test_timestamp_now() { 20 | let timestamp = Timestamp.now() 21 | XCTAssertGreaterThan(timestamp.microsSinceEpoch, 1_595_592_205_693_986) 22 | XCTAssertGreaterThan(timestamp.millisSinceEpoch, 1_595_592_205_693) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracedLock.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | import Foundation 16 | import Instrumentation 17 | import Tracing 18 | 19 | final class TracedLock { 20 | let name: String 21 | let underlyingLock: NSLock 22 | 23 | var activeSpan: Span? 24 | 25 | init(name: String) { 26 | self.name = name 27 | self.underlyingLock = NSLock() 28 | } 29 | 30 | func lock(baggage: Baggage) { 31 | // time here 32 | self.underlyingLock.lock() 33 | self.activeSpan = InstrumentationSystem.tracer.startSpan(named: self.name, baggage: baggage) 34 | } 35 | 36 | func unlock(baggage: Baggage) { 37 | self.activeSpan?.end() 38 | self.activeSpan = nil 39 | self.underlyingLock.unlock() 40 | } 41 | 42 | func withLock(baggage: Baggage, _ closure: () -> Void) { 43 | self.lock(baggage: baggage) 44 | defer { self.unlock(baggage: baggage) } 45 | closure() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracedLockTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // TracedLockTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension TracedLockTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (TracedLockTests) -> () throws -> Void)] { 27 | return [ 28 | ("test_tracesLockedTime", test_tracesLockedTime), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracedLockTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import Baggage 15 | @testable import Instrumentation 16 | import Tracing 17 | import XCTest 18 | 19 | final class TracedLockTests: XCTestCase { 20 | override class func tearDown() { 21 | super.tearDown() 22 | InstrumentationSystem.bootstrapInternal(nil) 23 | } 24 | 25 | func test_tracesLockedTime() { 26 | let tracer = TracedLockPrintlnTracer() 27 | InstrumentationSystem.bootstrapInternal(tracer) 28 | 29 | let lock = TracedLock(name: "my-cool-lock") 30 | 31 | func launchTask(_ name: String) { 32 | DispatchQueue.global().async { 33 | var baggage = Baggage.topLevel 34 | baggage[TaskIDKey.self] = name 35 | 36 | lock.lock(baggage: baggage) 37 | lock.unlock(baggage: baggage) 38 | } 39 | } 40 | launchTask("one") 41 | launchTask("two") 42 | launchTask("three") 43 | launchTask("four") 44 | 45 | Thread.sleep(forTimeInterval: 1) 46 | } 47 | } 48 | 49 | // ==== ------------------------------------------------------------------------ 50 | // MARK: test keys 51 | 52 | enum TaskIDKey: Baggage.Key { 53 | typealias Value = String 54 | static let name: String? = "LockedOperationNameKey" 55 | } 56 | 57 | // ==== ------------------------------------------------------------------------ 58 | // MARK: PrintLn Tracer 59 | 60 | private final class TracedLockPrintlnTracer: Tracer { 61 | func startSpan( 62 | named operationName: String, 63 | baggage: Baggage, 64 | ofKind kind: SpanKind, 65 | at timestamp: Timestamp 66 | ) -> Span { 67 | return TracedLockPrintlnSpan( 68 | operationName: operationName, 69 | startTimestamp: timestamp, 70 | kind: kind, 71 | baggage: baggage 72 | ) 73 | } 74 | 75 | public func forceFlush() {} 76 | 77 | func inject( 78 | _ baggage: Baggage, 79 | into carrier: inout Carrier, 80 | using injector: Injector 81 | ) 82 | where 83 | Injector: InjectorProtocol, 84 | Carrier == Injector.Carrier {} 85 | 86 | func extract( 87 | _ carrier: Carrier, 88 | into baggage: inout Baggage, 89 | using extractor: Extractor 90 | ) 91 | where 92 | Extractor: ExtractorProtocol, 93 | Carrier == Extractor.Carrier {} 94 | 95 | final class TracedLockPrintlnSpan: Span { 96 | private let operationName: String 97 | private let kind: SpanKind 98 | 99 | private var status: SpanStatus? 100 | 101 | private let startTimestamp: Timestamp 102 | private(set) var endTimestamp: Timestamp? 103 | 104 | let baggage: Baggage 105 | 106 | private var links = [SpanLink]() 107 | 108 | private var events = [SpanEvent]() { 109 | didSet { 110 | self.isRecording = !self.events.isEmpty 111 | } 112 | } 113 | 114 | var attributes: SpanAttributes = [:] { 115 | didSet { 116 | self.isRecording = !self.attributes.isEmpty 117 | } 118 | } 119 | 120 | private(set) var isRecording = false 121 | 122 | init( 123 | operationName: String, 124 | startTimestamp: Timestamp, 125 | kind: SpanKind, 126 | baggage: Baggage 127 | ) { 128 | self.operationName = operationName 129 | self.startTimestamp = startTimestamp 130 | self.baggage = baggage 131 | self.kind = kind 132 | 133 | print(" span [\(self.operationName): \(self.baggage[TaskIDKey.self] ?? "no-name")] @ \(self.startTimestamp): start") 134 | } 135 | 136 | func setStatus(_ status: SpanStatus) { 137 | self.status = status 138 | self.isRecording = true 139 | } 140 | 141 | func addLink(_ link: SpanLink) { 142 | self.links.append(link) 143 | } 144 | 145 | func addEvent(_ event: SpanEvent) { 146 | self.events.append(event) 147 | } 148 | 149 | func recordError(_ error: Error) {} 150 | 151 | func end(at timestamp: Timestamp) { 152 | self.endTimestamp = timestamp 153 | print(" span [\(self.operationName): \(self.baggage[TaskIDKey.self] ?? "no-name")] @ \(timestamp): end") 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracerTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // TracerTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension TracerTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (TracerTests) -> () throws -> Void)] { 27 | return [ 28 | ("testContextPropagation", testContextPropagation), 29 | ("testContextPropagationWithNoOpSpan", testContextPropagationWithNoOpSpan), 30 | ] 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracerTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | @testable import Instrumentation 16 | import Tracing 17 | import XCTest 18 | 19 | final class TracerTests: XCTestCase { 20 | override class func tearDown() { 21 | super.tearDown() 22 | InstrumentationSystem.bootstrapInternal(nil) 23 | } 24 | 25 | func testContextPropagation() { 26 | let tracer = TestTracer() 27 | InstrumentationSystem.bootstrap(tracer) 28 | 29 | let httpServer = FakeHTTPServer { context, _, client -> FakeHTTPResponse in 30 | client.performRequest(context, request: FakeHTTPRequest(path: "/test", headers: [])) 31 | return FakeHTTPResponse(status: 418) 32 | } 33 | 34 | httpServer.receive(FakeHTTPRequest(path: "/", headers: [("trace-id", "test")])) 35 | 36 | XCTAssertEqual(tracer.spans.count, 2) 37 | for span in tracer.spans { 38 | XCTAssertEqual(span.baggage.traceID, "test") 39 | } 40 | } 41 | 42 | func testContextPropagationWithNoOpSpan() { 43 | let httpServer = FakeHTTPServer { _, _, client -> FakeHTTPResponse in 44 | var baggage = Baggage.topLevel 45 | baggage.traceID = "test" 46 | client.performRequest(baggage, request: FakeHTTPRequest(path: "/test", headers: [])) 47 | return FakeHTTPResponse(status: 418) 48 | } 49 | 50 | httpServer.receive(FakeHTTPRequest(path: "/", headers: [("trace-id", "test")])) 51 | 52 | XCTAssertEqual(httpServer.client.baggages.count, 1) 53 | XCTAssertEqual(httpServer.client.baggages.first?.traceID, "test") 54 | } 55 | } 56 | 57 | // MARK: - Fake HTTP Server 58 | 59 | typealias HTTPHeaders = [(String, String)] 60 | 61 | struct HTTPHeadersExtractor: ExtractorProtocol { 62 | func extract(key: String, from headers: HTTPHeaders) -> String? { 63 | return headers.first(where: { $0.0 == key })?.1 64 | } 65 | } 66 | 67 | struct HTTPHeadersInjector: InjectorProtocol { 68 | func inject(_ value: String, forKey key: String, into headers: inout HTTPHeaders) { 69 | headers.append((key, value)) 70 | } 71 | } 72 | 73 | struct FakeHTTPRequest { 74 | let path: String 75 | var headers: HTTPHeaders 76 | } 77 | 78 | struct FakeHTTPResponse { 79 | let status: Int 80 | } 81 | 82 | struct FakeHTTPServer { 83 | typealias Handler = (Baggage, FakeHTTPRequest, FakeHTTPClient) -> FakeHTTPResponse 84 | 85 | private let catchAllHandler: Handler 86 | let client: FakeHTTPClient 87 | 88 | init(catchAllHandler: @escaping Handler) { 89 | self.catchAllHandler = catchAllHandler 90 | self.client = FakeHTTPClient() 91 | } 92 | 93 | func receive(_ request: FakeHTTPRequest) { 94 | let tracer = InstrumentationSystem.tracer 95 | 96 | var baggage = Baggage.topLevel 97 | InstrumentationSystem.instrument.extract(request.headers, into: &baggage, using: HTTPHeadersExtractor()) 98 | 99 | let span = tracer.startSpan(named: "GET \(request.path)", baggage: baggage) 100 | 101 | let response = self.catchAllHandler(span.baggage, request, self.client) 102 | span.attributes["http.status"] = .int(response.status) 103 | 104 | span.end() 105 | } 106 | } 107 | 108 | // MARK: - Fake HTTP Client 109 | 110 | final class FakeHTTPClient { 111 | private(set) var baggages = [Baggage]() 112 | 113 | func performRequest(_ baggage: Baggage, request: FakeHTTPRequest) { 114 | var request = request 115 | let span = InstrumentationSystem.tracer.startSpan(named: "GET \(request.path)", baggage: baggage) 116 | self.baggages.append(span.baggage) 117 | InstrumentationSystem.instrument.inject(baggage, into: &request.headers, using: HTTPHeadersInjector()) 118 | span.end() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracingInstrumentationSystemTests+XCTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | // 14 | // TracingInstrumentationSystemTests+XCTest.swift 15 | // 16 | import XCTest 17 | /// 18 | /// NOTE: This file was generated by generate_linux_tests.rb 19 | /// 20 | /// Do NOT edit this file directly as it will be regenerated automatically when needed. 21 | /// 22 | 23 | extension TracingInstrumentationSystemTests { 24 | 25 | @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") 26 | static var allTests : [(String, (TracingInstrumentationSystemTests) -> () throws -> Void)] { 27 | return [ 28 | ("testItProvidesAccessToATracer", testItProvidesAccessToATracer), 29 | ] 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Tests/TracingTests/TracingInstrumentationSystemTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | @testable import Instrumentation 15 | import Tracing 16 | import XCTest 17 | 18 | final class TracingInstrumentationSystemTests: XCTestCase { 19 | override class func tearDown() { 20 | super.tearDown() 21 | InstrumentationSystem.bootstrapInternal(nil) 22 | } 23 | 24 | func testItProvidesAccessToATracer() { 25 | let tracer = TestTracer() 26 | 27 | XCTAssertNil(InstrumentationSystem.tracer(of: TestTracer.self)) 28 | 29 | InstrumentationSystem.bootstrapInternal(tracer) 30 | XCTAssertFalse(InstrumentationSystem.instrument is MultiplexInstrument) 31 | XCTAssert(InstrumentationSystem.instrument(of: TestTracer.self) === tracer) 32 | XCTAssertNil(InstrumentationSystem.instrument(of: NoOpInstrument.self)) 33 | 34 | XCTAssert(InstrumentationSystem.tracer(of: TestTracer.self) === tracer) 35 | XCTAssert(InstrumentationSystem.tracer is TestTracer) 36 | 37 | let multiplexInstrument = MultiplexInstrument([tracer]) 38 | InstrumentationSystem.bootstrapInternal(multiplexInstrument) 39 | XCTAssert(InstrumentationSystem.instrument is MultiplexInstrument) 40 | XCTAssert(InstrumentationSystem.instrument(of: TestTracer.self) === tracer) 41 | 42 | XCTAssert(InstrumentationSystem.tracer(of: TestTracer.self) === tracer) 43 | XCTAssert(InstrumentationSystem.tracer is TestTracer) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UseCases/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm 7 | /Package.resolved 8 | /Sources/InstrumentsAppTracingInstrpkg/SpansExampleInstrument/SpansExampleInstrument.xcodeproj/project.xcworkspace/ -------------------------------------------------------------------------------- /UseCases/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "use-cases", 6 | products: [ 7 | .executable(name: "ManualContextPropagation", targets: ["ManualContextPropagation"]), 8 | .executable(name: "ManualAsyncHTTPClient", targets: ["ManualAsyncHTTPClient"]), 9 | .executable(name: "HTTPEndToEnd", targets: ["HTTPEndToEnd"]), 10 | .executable(name: "InstrumentsAppTracing", targets: ["InstrumentsAppTracing"]), 11 | ], 12 | dependencies: [ 13 | .package(path: "../"), 14 | .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.1.1"), 15 | .package(url: "https://github.com/slashmo/swift-nio.git", .branch("feature/baggage-context")), 16 | .package( 17 | url: "https://github.com/slashmo/gsoc-swift-baggage-context.git", 18 | from: "0.5.0" 19 | ), 20 | ], 21 | targets: [ 22 | .target(name: "ManualContextPropagation", dependencies: [ 23 | "Instrumentation", 24 | "Baggage", 25 | ]), 26 | .target(name: "ManualAsyncHTTPClient", dependencies: [ 27 | "Instrumentation", 28 | "NIOInstrumentation", 29 | "AsyncHTTPClient", 30 | "NIO", 31 | "Baggage", 32 | ]), 33 | .target(name: "HTTPEndToEnd", dependencies: [ 34 | "Tracing", 35 | "NIOInstrumentation", 36 | "AsyncHTTPClient", 37 | "NIO", 38 | "Baggage", 39 | ]), 40 | .target(name: "InstrumentsAppTracing", dependencies: [ 41 | "Instrumentation", 42 | "Tracing", 43 | ]), 44 | ] 45 | ) 46 | -------------------------------------------------------------------------------- /UseCases/Sources/HTTPEndToEnd/InstrumentedHTTPClient.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import AsyncHTTPClient 15 | import BaggageContext 16 | import Instrumentation 17 | import Logging 18 | import NIO 19 | import NIOHTTP1 20 | import NIOInstrumentation 21 | 22 | struct InstrumentedHTTPClient { 23 | private let client: HTTPClient 24 | private let instrument: Instrument 25 | private let logger = Logger(label: "InstrumentedHTTPClient") 26 | 27 | init(instrument: Instrument, eventLoopGroupProvider: HTTPClient.EventLoopGroupProvider) { 28 | self.client = HTTPClient(eventLoopGroupProvider: eventLoopGroupProvider) 29 | self.instrument = instrument 30 | } 31 | 32 | // TODO: deadline: NIODeadline? would move into baggage? 33 | public func get(url: String, context: BaggageContext) -> EventLoopFuture { 34 | do { 35 | let request = try HTTPClient.Request(url: url, method: .GET) 36 | return self.execute(request: request, context: context) 37 | } catch { 38 | return self.client.eventLoopGroup.next().makeFailedFuture(error) 39 | } 40 | } 41 | 42 | func execute(request: HTTPClient.Request, context: BaggageContext) -> EventLoopFuture { 43 | var request = request 44 | self.instrument.inject(context.baggage, into: &request.headers, using: HTTPHeadersInjector()) 45 | context.logger.info("🌎 InstrumentedHTTPClient: Execute request") 46 | return self.client.execute(request: request) 47 | } 48 | 49 | func syncShutdown() throws { 50 | try self.client.syncShutdown() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /UseCases/Sources/HTTPEndToEnd/README.md: -------------------------------------------------------------------------------- 1 | # HTTP End-to-end 2 | 3 | This use-case demonstrates how both `BaggageContext` and `Instrument` may work in an end-to-end scenario. The example 4 | defines two services: 5 | 6 | - 🧾 `OrderService` 7 | - 📦 `StorageService` 8 | 9 | `BaggageLogging` is used throught the example to automatically add the `BaggageContext` contents to the `Logger` being used. 10 | 11 | ## Steps 12 | 13 | 1. `AsyncHTTPClient` used to make a request to the order service 14 | 2. On receive, order service uses `FakeTracer` to extract trace information into the `BaggageContext` 15 | 3. Because no trace ID exists at that point, `FakeTracer` will generate a new one to be stored in the `BaggageContext` 16 | 4. The order service makes an HTTP request to the storage service (using `InstrumentedHTTPClient`) 17 | 5. `InstrumentedHTTPClient` uses `FakeTracer` to automatically inject the trace ID into the request headers 18 | 6. On receive, storage service uses `FakeTracer` to extract trace information into the `BaggageContext` 19 | -------------------------------------------------------------------------------- /UseCases/Sources/HTTPEndToEnd/Services/OrderServiceHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import AsyncHTTPClient 15 | import BaggageContext 16 | import Instrumentation 17 | import Logging 18 | import NIO 19 | import NIOHTTP1 20 | import NIOInstrumentation 21 | 22 | final class OrderServiceHandler: ChannelInboundHandler { 23 | typealias InboundIn = HTTPServerRequestPart 24 | typealias OutboundOut = HTTPServerResponsePart 25 | 26 | private let httpClient: InstrumentedHTTPClient 27 | private let instrument: Instrument 28 | private let logger = Logger(label: "OrderService") 29 | 30 | init(httpClient: InstrumentedHTTPClient, instrument: Instrument) { 31 | self.httpClient = httpClient 32 | self.instrument = instrument 33 | } 34 | 35 | func channelRead(context: ChannelHandlerContext, data: NIOAny) { 36 | guard case .head(let requestHead) = self.unwrapInboundIn(data) else { return } 37 | 38 | var ctx = DefaultContext(baggage: .topLevel, logger: self.logger) 39 | 40 | self.instrument.extract(requestHead.headers, into: &ctx.baggage, using: HTTPHeadersExtractor()) 41 | 42 | ctx.logger.info("🧾 Received order request") 43 | 44 | context.eventLoop.scheduleTask(in: .seconds(1)) { 45 | ctx.logger.info("🧾 Asking StorageService if your product exists") 46 | 47 | let request = try! HTTPClient.Request(url: "http://localhost:8081") 48 | self.httpClient.execute(request: request, context: ctx).whenComplete { _ in 49 | let responseHead = HTTPResponseHead(version: requestHead.version, status: .created) 50 | context.eventLoop.execute { 51 | context.channel.write(self.wrapOutboundOut(.head(responseHead)), promise: nil) 52 | context.channel.write(self.wrapOutboundOut(.end(nil)), promise: nil) 53 | context.channel.flush() 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /UseCases/Sources/HTTPEndToEnd/Services/StorageServiceHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | import Instrumentation 16 | import Logging 17 | import NIO 18 | import NIOHTTP1 19 | import NIOInstrumentation 20 | 21 | final class StorageServiceHandler: ChannelInboundHandler { 22 | typealias InboundIn = HTTPServerRequestPart 23 | typealias OutboundOut = HTTPServerResponsePart 24 | 25 | private let logger = Logger(label: "StorageService") 26 | private let instrument: Instrument 27 | 28 | init(instrument: Instrument) { 29 | self.instrument = instrument 30 | } 31 | 32 | func channelRead(context: ChannelHandlerContext, data: NIOAny) { 33 | guard case .head(let requestHead) = self.unwrapInboundIn(data) else { return } 34 | 35 | var ctx = DefaultContext(baggage: .topLevel, logger: logger) 36 | self.instrument.extract(requestHead.headers, into: &ctx.baggage, using: HTTPHeadersExtractor()) 37 | 38 | ctx.logger.info("📦 Looking for the product") 39 | 40 | context.eventLoop.scheduleTask(in: .seconds(2)) { 41 | ctx.logger.info("📦 Found the product") 42 | let responseHead = HTTPResponseHead(version: requestHead.version, status: .ok) 43 | context.eventLoop.execute { 44 | context.channel.write(self.wrapOutboundOut(.head(responseHead)), promise: nil) 45 | context.channel.write(self.wrapOutboundOut(.end(nil)), promise: nil) 46 | context.channel.flush() 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UseCases/Sources/HTTPEndToEnd/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import AsyncHTTPClient 15 | import BaggageContext 16 | import Foundation 17 | import Instrumentation 18 | import Logging 19 | import NIO 20 | import NIOHTTP1 21 | 22 | // MARK: - Setups 23 | 24 | func serviceBootstrap(handler: ChannelHandler) -> ServerBootstrap { 25 | return ServerBootstrap(group: eventLoopGroup) 26 | .serverChannelOption(ChannelOptions.backlog, value: 256) 27 | .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) 28 | .childChannelInitializer { channel in 29 | channel.pipeline.configureHTTPServerPipeline().flatMap { 30 | channel.pipeline.addHandler(handler) 31 | } 32 | } 33 | .childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) 34 | .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 1) 35 | .childChannelOption(ChannelOptions.allowRemoteHalfClosure, value: true) 36 | } 37 | 38 | // MARK: - Fake Tracer 39 | 40 | private final class FakeTracer: Instrument { 41 | enum TraceIDKey: Baggage.Key { 42 | typealias Value = String 43 | } 44 | 45 | static let headerName = "fake-trace-id" 46 | static let defaultTraceID = UUID().uuidString 47 | 48 | func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Injector) 49 | where 50 | Injector: InjectorProtocol, 51 | Carrier == Injector.Carrier 52 | { 53 | guard let traceID = baggage[TraceIDKey.self] else { return } 54 | injector.inject(traceID, forKey: FakeTracer.headerName, into: &carrier) 55 | } 56 | 57 | func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor) 58 | where 59 | Extractor: ExtractorProtocol, 60 | Carrier == Extractor.Carrier 61 | { 62 | let traceID = extractor.extract(key: FakeTracer.headerName, from: carrier) ?? FakeTracer.defaultTraceID 63 | baggage[TraceIDKey.self] = traceID 64 | } 65 | } 66 | 67 | // MARK: - Demo 68 | 69 | let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) 70 | 71 | let httpClient = InstrumentedHTTPClient(instrument: FakeTracer(), eventLoopGroupProvider: .createNew) 72 | 73 | let orderServiceBootstrap = serviceBootstrap( 74 | handler: OrderServiceHandler(httpClient: httpClient, instrument: FakeTracer()) 75 | ) 76 | let storageServiceBootstrap = serviceBootstrap( 77 | handler: StorageServiceHandler(instrument: FakeTracer()) 78 | ) 79 | 80 | let logger = Logger(label: "FruitStore") 81 | 82 | let orderServiceChannel = try orderServiceBootstrap.bind(host: "localhost", port: 8080).wait() 83 | logger.info("🧾 Order service listening on ::1:8080") 84 | 85 | let storageServiceChannel = try storageServiceBootstrap.bind(host: "localhost", port: 8081).wait() 86 | logger.info("📦 Storage service listening on ::1:8081") 87 | 88 | logger.info("💻 Placing order") 89 | httpClient.get(url: "http://localhost:8080", context: DefaultContext(baggage: .topLevel, logger: logger)).whenComplete { _ in 90 | logger.info("💻 Order completed") 91 | } 92 | 93 | sleep(5) 94 | 95 | try httpClient.syncShutdown() 96 | try orderServiceChannel.close().wait() 97 | try storageServiceChannel.close().wait() 98 | try eventLoopGroup.syncShutdownGracefully() 99 | -------------------------------------------------------------------------------- /UseCases/Sources/InstrumentsAppTracing/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | import Dispatch 16 | import Instrumentation 17 | import Tracing 18 | 19 | // ==== ---------------------------------------------------------------------------------------------------------------- 20 | // MARK: Setup 21 | #if os(macOS) || os(tvOS) || os(iOS) || os(watchOS) 22 | 23 | if #available(macOS 10.14, iOS 10.0, *) { 24 | let signpostTracing = OSSignpostTracingInstrument( 25 | subsystem: "org.swift.server.tracing.example", 26 | category: "Example", 27 | signpostName: "TracingSpans" 28 | ) 29 | InstrumentationSystem.bootstrap(signpostTracing) 30 | } else { 31 | fatalError("Available only on Apple platforms.") 32 | } 33 | 34 | // ==== ---------------------------------------------------------------------------------------------------------------- 35 | // MARK: Manual usage 36 | 37 | let tracer = InstrumentationSystem.tracer 38 | let context = DefaultContext(baggage: .topLevel, logger: .init(label: "test")) 39 | 40 | for i in 1 ... 5 { 41 | print("Starting operation: op-\(i)") 42 | let parentSpan = tracer.startSpan(named: "op-\(i)", baggage: context.baggage) 43 | defer { parentSpan.end() } 44 | 45 | DispatchQueue.global().async { 46 | let span = tracer.startSpan(named: "op-\(i)-inner", baggage: context.baggage) 47 | span.addLink(parentSpan) 48 | print(" Starting sub-operation: op-\(i)-inner") 49 | defer { span.end() } 50 | 51 | sleep(UInt32(1)) 52 | } 53 | // TODO: Thread.run { let some child span here } 54 | 55 | sleep(UInt32(i)) 56 | } 57 | 58 | print("done.") 59 | 60 | #else 61 | print("Demo only available on macOS / Apple platforms.") 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /UseCases/Sources/InstrumentsAppTracingInstrpkg/SpansExampleInstrument/SpansExampleInstrument.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B9FC5FF924CD3AB1007CB118 /* SpansExampleInstrument.instrpkg in Sources */ = {isa = PBXBuildFile; fileRef = B9FC5FF824CD3AB1007CB118 /* SpansExampleInstrument.instrpkg */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXFileReference section */ 14 | B9FC5FF524CD3AB1007CB118 /* SpansExampleInstrument.instrdst */ = {isa = PBXFileReference; explicitFileType = com.apple.instruments.instrdst; includeInIndex = 0; path = SpansExampleInstrument.instrdst; sourceTree = BUILT_PRODUCTS_DIR; }; 15 | B9FC5FF824CD3AB1007CB118 /* SpansExampleInstrument.instrpkg */ = {isa = PBXFileReference; lastKnownFileType = "com.apple.instruments.package-definition"; path = SpansExampleInstrument.instrpkg; sourceTree = ""; }; 16 | /* End PBXFileReference section */ 17 | 18 | /* Begin PBXGroup section */ 19 | B9FC5FEE24CD3AB1007CB118 = { 20 | isa = PBXGroup; 21 | children = ( 22 | B9FC5FF724CD3AB1007CB118 /* SpansExampleInstrument */, 23 | B9FC5FF624CD3AB1007CB118 /* Products */, 24 | ); 25 | sourceTree = ""; 26 | }; 27 | B9FC5FF624CD3AB1007CB118 /* Products */ = { 28 | isa = PBXGroup; 29 | children = ( 30 | B9FC5FF524CD3AB1007CB118 /* SpansExampleInstrument.instrdst */, 31 | ); 32 | name = Products; 33 | sourceTree = ""; 34 | }; 35 | B9FC5FF724CD3AB1007CB118 /* SpansExampleInstrument */ = { 36 | isa = PBXGroup; 37 | children = ( 38 | B9FC5FF824CD3AB1007CB118 /* SpansExampleInstrument.instrpkg */, 39 | ); 40 | path = SpansExampleInstrument; 41 | sourceTree = ""; 42 | }; 43 | /* End PBXGroup section */ 44 | 45 | /* Begin PBXNativeTarget section */ 46 | B9FC5FF424CD3AB1007CB118 /* SpansExampleInstrument */ = { 47 | isa = PBXNativeTarget; 48 | buildConfigurationList = B9FC5FFA24CD3AB1007CB118 /* Build configuration list for PBXNativeTarget "SpansExampleInstrument" */; 49 | buildPhases = ( 50 | B9FC5FF324CD3AB1007CB118 /* Sources */, 51 | ); 52 | buildRules = ( 53 | ); 54 | dependencies = ( 55 | ); 56 | name = SpansExampleInstrument; 57 | productName = SpansExampleInstrument; 58 | productReference = B9FC5FF524CD3AB1007CB118 /* SpansExampleInstrument.instrdst */; 59 | productType = "com.apple.product-type.instruments-package"; 60 | }; 61 | /* End PBXNativeTarget section */ 62 | 63 | /* Begin PBXProject section */ 64 | B9FC5FEF24CD3AB1007CB118 /* Project object */ = { 65 | isa = PBXProject; 66 | attributes = { 67 | LastUpgradeCheck = 1200; 68 | TargetAttributes = { 69 | B9FC5FF424CD3AB1007CB118 = { 70 | CreatedOnToolsVersion = 12.0; 71 | }; 72 | }; 73 | }; 74 | buildConfigurationList = B9FC5FF224CD3AB1007CB118 /* Build configuration list for PBXProject "SpansExampleInstrument" */; 75 | compatibilityVersion = "Xcode 9.3"; 76 | developmentRegion = en; 77 | hasScannedForEncodings = 0; 78 | knownRegions = ( 79 | en, 80 | Base, 81 | ); 82 | mainGroup = B9FC5FEE24CD3AB1007CB118; 83 | productRefGroup = B9FC5FF624CD3AB1007CB118 /* Products */; 84 | projectDirPath = ""; 85 | projectRoot = ""; 86 | targets = ( 87 | B9FC5FF424CD3AB1007CB118 /* SpansExampleInstrument */, 88 | ); 89 | }; 90 | /* End PBXProject section */ 91 | 92 | /* Begin PBXSourcesBuildPhase section */ 93 | B9FC5FF324CD3AB1007CB118 /* Sources */ = { 94 | isa = PBXSourcesBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | B9FC5FF924CD3AB1007CB118 /* SpansExampleInstrument.instrpkg in Sources */, 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | /* End PBXSourcesBuildPhase section */ 102 | 103 | /* Begin XCBuildConfiguration section */ 104 | B9FC5FF024CD3AB1007CB118 /* Debug */ = { 105 | isa = XCBuildConfiguration; 106 | buildSettings = { 107 | COPY_PHASE_STRIP = NO; 108 | }; 109 | name = Debug; 110 | }; 111 | B9FC5FF124CD3AB1007CB118 /* Release */ = { 112 | isa = XCBuildConfiguration; 113 | buildSettings = { 114 | COPY_PHASE_STRIP = YES; 115 | }; 116 | name = Release; 117 | }; 118 | B9FC5FFB24CD3AB1007CB118 /* Debug */ = { 119 | isa = XCBuildConfiguration; 120 | buildSettings = { 121 | ALWAYS_SEARCH_USER_PATHS = NO; 122 | CODE_SIGN_STYLE = Automatic; 123 | DEVELOPMENT_TEAM = BALXGG2H6J; 124 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Instruments/Packages"; 125 | PRODUCT_NAME = "$(TARGET_NAME)"; 126 | VERSIONING_SYSTEM = ""; 127 | }; 128 | name = Debug; 129 | }; 130 | B9FC5FFC24CD3AB1007CB118 /* Release */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CODE_SIGN_STYLE = Automatic; 135 | DEVELOPMENT_TEAM = BALXGG2H6J; 136 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Instruments/Packages"; 137 | PRODUCT_NAME = "$(TARGET_NAME)"; 138 | VERSIONING_SYSTEM = ""; 139 | }; 140 | name = Release; 141 | }; 142 | /* End XCBuildConfiguration section */ 143 | 144 | /* Begin XCConfigurationList section */ 145 | B9FC5FF224CD3AB1007CB118 /* Build configuration list for PBXProject "SpansExampleInstrument" */ = { 146 | isa = XCConfigurationList; 147 | buildConfigurations = ( 148 | B9FC5FF024CD3AB1007CB118 /* Debug */, 149 | B9FC5FF124CD3AB1007CB118 /* Release */, 150 | ); 151 | defaultConfigurationIsVisible = 0; 152 | defaultConfigurationName = Release; 153 | }; 154 | B9FC5FFA24CD3AB1007CB118 /* Build configuration list for PBXNativeTarget "SpansExampleInstrument" */ = { 155 | isa = XCConfigurationList; 156 | buildConfigurations = ( 157 | B9FC5FFB24CD3AB1007CB118 /* Debug */, 158 | B9FC5FFC24CD3AB1007CB118 /* Release */, 159 | ); 160 | defaultConfigurationIsVisible = 0; 161 | defaultConfigurationName = Release; 162 | }; 163 | /* End XCConfigurationList section */ 164 | }; 165 | rootObject = B9FC5FEF24CD3AB1007CB118 /* Project object */; 166 | } 167 | -------------------------------------------------------------------------------- /UseCases/Sources/InstrumentsAppTracingInstrpkg/SpansExampleInstrument/SpansExampleInstrument/SpansExampleInstrument.instrpkg: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.swift.server.tracing.example.SpansExampleInstrument 4 | SpansExampleInstrument 5 | 6 | Konrad `ktoso` Malawski 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | tracing-spans 15 | Example: OSSignpost tracing swift-tracing Spans 16 | 17 | "org.swift.server.tracing.example" 18 | "Example" 19 | "TracingSpans" 20 | 21 | 22 | "b;id:" ?id ";parent-ids:" ?parent-ids ";op-name:" ?op-name 23 | 24 | 25 | "e;" 26 | 27 | 28 | 29 | span-id 30 | ID 31 | 32 | uint64 33 | ?id 34 | 35 | 36 | parent-ids 37 | Parent IDs 38 | string 39 | ?parent-ids 40 | 41 | 42 | 43 | op-name 44 | Operation Name 45 | string 46 | ?op-name 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apple.swift.server.tracing.example.span-intervals 67 | Swift-Tracing: Spans 68 | Behavior 69 | Visualizes Span intervals, captured via os_signposts as emitted via the swift-tracing OSSignpost backend. 70 | Activity Monitor 71 | 72 | 73 | tracing-spans-table 74 | tracing-spans 75 | 76 | 77 | 78 | Spans 79 | 80 | 81 | Span Intervals 82 | tracing-spans-table 83 | 84 | op-name 85 | %s 86 | duration 87 | op-name 88 | 89 | 90 | 91 | 92 | List: Spans 93 | tracing-spans-table 94 | start 95 | op-name 96 | duration 97 | span-id 98 | parent-ids 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /UseCases/Sources/InstrumentsAppTracingInstrpkg/SpansExampleInstrument/SpansExampleInstrument/SpansExampleInstrumentTemplate.tracetemplate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashmo/gsoc-swift-tracing/a8da5245ff83ac7b0daea5d83ce589f0a82d4708/UseCases/Sources/InstrumentsAppTracingInstrpkg/SpansExampleInstrument/SpansExampleInstrument/SpansExampleInstrumentTemplate.tracetemplate -------------------------------------------------------------------------------- /UseCases/Sources/InstrumentsAppTracingInstrpkg/custom instrument screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashmo/gsoc-swift-tracing/a8da5245ff83ac7b0daea5d83ce589f0a82d4708/UseCases/Sources/InstrumentsAppTracingInstrpkg/custom instrument screenshot.png -------------------------------------------------------------------------------- /UseCases/Sources/ManualAsyncHTTPClient/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import AsyncHTTPClient 15 | import BaggageContext 16 | import Foundation 17 | import Instrumentation 18 | import Logging 19 | import NIOHTTP1 20 | import NIOInstrumentation 21 | 22 | let logger = Logger(label: "usecase") 23 | 24 | // MARK: - InstrumentedHTTPClient 25 | 26 | struct InstrumentedHTTPClient { 27 | private let client = HTTPClient(eventLoopGroupProvider: .createNew) 28 | private let instrument: Instrument 29 | 30 | init(instrument: Instrument) { 31 | self.instrument = instrument 32 | } 33 | 34 | func execute(request: HTTPClient.Request, context: BaggageContext) { 35 | var request = request 36 | self.instrument.inject(context.baggage, into: &request.headers, using: HTTPHeadersInjector()) 37 | context.logger.info("Execute request using injected header values") 38 | print(request.headers) 39 | } 40 | } 41 | 42 | // MARK: - Fake HTTP Server 43 | 44 | struct FakeHTTPResponse {} 45 | 46 | struct FakeHTTPServer { 47 | typealias Handler = (BaggageContext, HTTPClient.Request, InstrumentedHTTPClient) -> FakeHTTPResponse 48 | 49 | private let instrument: Instrument 50 | private let catchAllHandler: Handler 51 | private let client: InstrumentedHTTPClient 52 | 53 | init(instrument: Instrument, catchAllHandler: @escaping Handler) { 54 | self.instrument = instrument 55 | self.catchAllHandler = catchAllHandler 56 | self.client = InstrumentedHTTPClient(instrument: instrument) 57 | } 58 | 59 | func receive(_ request: HTTPClient.Request) { 60 | var context = DefaultContext(baggage: .topLevel, logger: logger) 61 | context.logger.info("Extracting context values from request headers into context") 62 | self.instrument.extract(request.headers, into: &context.baggage, using: HTTPHeadersExtractor()) 63 | _ = self.catchAllHandler(context, request, self.client) 64 | } 65 | } 66 | 67 | // MARK: - Fake Tracer 68 | 69 | private final class FakeTracer: Instrument { 70 | enum TraceIDKey: Baggage.Key { 71 | typealias Value = String 72 | } 73 | 74 | static let headerName = "fake-trace-id" 75 | static let defaultTraceID = UUID().uuidString 76 | 77 | func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Injector) 78 | where 79 | Injector: InjectorProtocol, 80 | Carrier == Injector.Carrier 81 | { 82 | guard let traceID = baggage[TraceIDKey.self] else { return } 83 | injector.inject(traceID, forKey: FakeTracer.headerName, into: &carrier) 84 | } 85 | 86 | func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor) 87 | where 88 | Extractor: ExtractorProtocol, 89 | Carrier == Extractor.Carrier 90 | { 91 | let traceID = extractor.extract(key: FakeTracer.headerName, from: carrier) ?? FakeTracer.defaultTraceID 92 | baggage[TraceIDKey.self] = traceID 93 | } 94 | } 95 | 96 | // MARK: - Demo 97 | 98 | let server = FakeHTTPServer( 99 | instrument: FakeTracer() 100 | ) { context, _, client -> FakeHTTPResponse in 101 | context.logger.info("Perform subsequent request") 102 | let outgoingRequest = try! HTTPClient.Request( 103 | url: "https://swift.org", 104 | headers: ["Accept": "application/json"] 105 | ) 106 | client.execute(request: outgoingRequest, context: context) 107 | return FakeHTTPResponse() 108 | } 109 | 110 | logger.info("Receive HTTP request on server") 111 | server.receive(try! HTTPClient.Request(url: "https://swift.org")) 112 | -------------------------------------------------------------------------------- /UseCases/Sources/ManualContextPropagation/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift Tracing open source project 4 | // 5 | // Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 6 | // Licensed under Apache License v2.0 7 | // 8 | // See LICENSE.txt for license information 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | import BaggageContext 15 | import Foundation 16 | import Instrumentation 17 | 18 | // MARK: - Fake HTTP Server 19 | 20 | typealias HTTPHeaders = [(String, String)] 21 | 22 | struct HTTPHeadersExtractor: ExtractorProtocol { 23 | func extract(key: String, from headers: HTTPHeaders) -> String? { 24 | return headers.first(where: { $0.0 == key })?.1 25 | } 26 | } 27 | 28 | struct HTTPHeadersInjector: InjectorProtocol { 29 | func inject(_ value: String, forKey key: String, into headers: inout HTTPHeaders) { 30 | headers.append((key, value)) 31 | } 32 | } 33 | 34 | struct FakeHTTPRequest { 35 | let path: String 36 | var headers: HTTPHeaders 37 | } 38 | 39 | struct FakeHTTPResponse {} 40 | 41 | struct FakeHTTPServer { 42 | typealias Handler = (BaggageContext, FakeHTTPRequest, FakeHTTPClient) -> FakeHTTPResponse 43 | 44 | private let instrument: Instrument 45 | private let catchAllHandler: Handler 46 | private let client: FakeHTTPClient 47 | 48 | init(instrument: Instrument, catchAllHandler: @escaping Handler) { 49 | self.instrument = instrument 50 | self.catchAllHandler = catchAllHandler 51 | self.client = FakeHTTPClient(instrument: instrument) 52 | } 53 | 54 | func receive(_ request: FakeHTTPRequest) { 55 | var context = DefaultContext(baggage: .topLevel, logger: .init(label: "test")) 56 | context.logger.info("\(String(describing: FakeHTTPRequest.self)): Extracting context values from request headers into context") 57 | self.instrument.extract(request.headers, into: &context.baggage, using: HTTPHeadersExtractor()) 58 | _ = self.catchAllHandler(context, request, self.client) 59 | } 60 | } 61 | 62 | // MARK: - Fake HTTP Client 63 | 64 | struct FakeHTTPClient { 65 | private let instrument: Instrument 66 | 67 | init(instrument: Instrument) { 68 | self.instrument = instrument 69 | } 70 | 71 | func performRequest(_ context: BaggageContext, request: FakeHTTPRequest) { 72 | var request = request 73 | context.logger.info("\(String(describing: FakeHTTPClient.self)): Injecting context values into request headers") 74 | self.instrument.inject(context.baggage, into: &request.headers, using: HTTPHeadersInjector()) 75 | print(request) 76 | } 77 | } 78 | 79 | // MARK: - Fake Tracer 80 | 81 | private final class FakeTracer: Instrument { 82 | enum TraceIDKey: Baggage.Key { 83 | typealias Value = String 84 | } 85 | 86 | static let headerName = "fake-trace-id" 87 | static let defaultTraceID = UUID().uuidString 88 | 89 | func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Injector) 90 | where 91 | Injector: InjectorProtocol, 92 | Carrier == Injector.Carrier 93 | { 94 | guard let traceID = baggage[TraceIDKey.self] else { return } 95 | injector.inject(traceID, forKey: FakeTracer.headerName, into: &carrier) 96 | } 97 | 98 | func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extractor) 99 | where 100 | Extractor: ExtractorProtocol, 101 | Carrier == Extractor.Carrier 102 | { 103 | let traceID = extractor.extract(key: FakeTracer.headerName, from: carrier) ?? FakeTracer.defaultTraceID 104 | baggage[TraceIDKey.self] = traceID 105 | } 106 | } 107 | 108 | // MARK: - Demo 109 | 110 | let server = FakeHTTPServer(instrument: FakeTracer()) { context, _, client in 111 | context.logger.info("Perform subsequent request") 112 | let outgoingRequest = FakeHTTPRequest(path: "/other-service", headers: [("Content-Type", "application/json")]) 113 | client.performRequest(context, request: outgoingRequest) 114 | return FakeHTTPResponse() 115 | } 116 | 117 | print("=== Receive HTTP request on server ===") 118 | server.receive(FakeHTTPRequest(path: "/", headers: [])) 119 | -------------------------------------------------------------------------------- /dev/Dockerfile-5.0.3: -------------------------------------------------------------------------------- 1 | FROM swift:5.0.3 2 | 3 | RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -y libz-dev 4 | -------------------------------------------------------------------------------- /dev/Dockerfile-5.1: -------------------------------------------------------------------------------- 1 | FROM swift:5.1 2 | -------------------------------------------------------------------------------- /dev/Dockerfile-5.2: -------------------------------------------------------------------------------- 1 | FROM swift:5.2 2 | -------------------------------------------------------------------------------- /images/zipkin_trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashmo/gsoc-swift-tracing/a8da5245ff83ac7b0daea5d83ce589f0a82d4708/images/zipkin_trace.png -------------------------------------------------------------------------------- /scripts/sanity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | set -eu 16 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 17 | 18 | printf "=> Checking linux tests... " 19 | FIRST_OUT="$(git status --porcelain)" 20 | ruby "$here/../scripts/generate_linux_tests.rb" > /dev/null 21 | SECOND_OUT="$(git status --porcelain)" 22 | if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then 23 | printf "\033[0;31mmissing changes!\033[0m\n" 24 | git --no-pager diff 25 | exit 1 26 | else 27 | printf "\033[0;32mokay.\033[0m\n" 28 | fi 29 | 30 | bash $here/validate_license_headers.sh 31 | bash $here/validate_format.sh 32 | bash $here/validate_naming.sh 33 | -------------------------------------------------------------------------------- /scripts/validate_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | set -u 16 | 17 | # verify that swiftformat is on the PATH 18 | command -v swiftformat >/dev/null 2>&1 || { echo >&2 "'swiftformat' could not be found. Please ensure it is installed and on the PATH."; exit 1; } 19 | 20 | printf "=> Checking format\n" 21 | FIRST_OUT="$(git status --porcelain)" 22 | # swiftformat does not scale so we loop ourselves 23 | shopt -u dotglob 24 | find Sources/* Tests/* IntegrationTests/* UseCases/* -type d | while IFS= read -r d; do 25 | printf " * checking $d... " 26 | out=$(swiftformat $d 2>&1) 27 | SECOND_OUT="$(git status --porcelain)" 28 | if [[ "$out" == *"error"*] && ["$out" != "*No eligible files" ]]; then 29 | printf "\033[0;31merror!\033[0m\n" 30 | echo $out 31 | exit 1 32 | fi 33 | if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then 34 | printf "\033[0;31mformatting issues!\033[0m\n" 35 | git --no-pager diff 36 | exit 1 37 | fi 38 | printf "\033[0;32mokay.\033[0m\n" 39 | done 40 | -------------------------------------------------------------------------------- /scripts/validate_license_headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | set -eu 30 | here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 31 | 32 | function replace_acceptable_years() { 33 | # this needs to replace all acceptable forms with 'YEARS' 34 | sed -e 's/2019-2020/YEARS/' -e 's/2020/YEARS/' 35 | } 36 | 37 | printf "=> Checking license headers\n" 38 | tmp=$(mktemp /tmp/.swift-baggage-context-sanity_XXXXXX) 39 | 40 | for language in swift-or-c bash dtrace; do 41 | printf " * $language... " 42 | declare -a matching_files 43 | declare -a exceptions 44 | expections=( ) 45 | matching_files=( -name '*' ) 46 | case "$language" in 47 | swift-or-c) 48 | exceptions=( -name c_nio_http_parser.c -o -name c_nio_http_parser.h -o -name cpp_magic.h -o -name Package.swift -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h) 49 | matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) 50 | cat > "$tmp" <<"EOF" 51 | //===----------------------------------------------------------------------===// 52 | // 53 | // This source file is part of the Swift Tracing open source project 54 | // 55 | // Copyright (c) YEARS Moritz Lang and the Swift Tracing project authors 56 | // Licensed under Apache License v2.0 57 | // 58 | // See LICENSE.txt for license information 59 | // 60 | // SPDX-License-Identifier: Apache-2.0 61 | // 62 | //===----------------------------------------------------------------------===// 63 | EOF 64 | ;; 65 | bash) 66 | matching_files=( -name '*.sh' ) 67 | cat > "$tmp" <<"EOF" 68 | #!/bin/bash 69 | ##===----------------------------------------------------------------------===## 70 | ## 71 | ## This source file is part of the Swift Tracing open source project 72 | ## 73 | ## Copyright (c) YEARS Moritz Lang and the Swift Tracing project authors 74 | ## Licensed under Apache License v2.0 75 | ## 76 | ## See LICENSE.txt for license information 77 | ## 78 | ## SPDX-License-Identifier: Apache-2.0 79 | ## 80 | ##===----------------------------------------------------------------------===## 81 | EOF 82 | ;; 83 | dtrace) 84 | matching_files=( -name '*.d' ) 85 | cat > "$tmp" <<"EOF" 86 | #!/usr/sbin/dtrace -q -s 87 | /*===----------------------------------------------------------------------===* 88 | * 89 | * This source file is part of the Swift Tracing open source project 90 | * 91 | * Copyright (c) YEARS Moritz Lang and the Swift Tracing project authors 92 | * Licensed under Apache License v2.0 93 | * 94 | * See LICENSE.txt for license information 95 | * 96 | * SPDX-License-Identifier: Apache-2.0 97 | * 98 | *===----------------------------------------------------------------------===*/ 99 | EOF 100 | ;; 101 | *) 102 | echo >&2 "ERROR: unknown language '$language'" 103 | ;; 104 | esac 105 | 106 | expected_lines=$(cat "$tmp" | wc -l) 107 | expected_sha=$(cat "$tmp" | shasum) 108 | 109 | ( 110 | cd "$here/.." 111 | find . \ 112 | \( \! -path './UseCases/.build/*' -a \ 113 | \( \! -path './.build/*' \) -a \ 114 | \( "${matching_files[@]}" \) -a \ 115 | \( \! \( "${exceptions[@]}" \) \) \) | while read line; do 116 | if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then 117 | printf "\033[0;31mmissing headers in file '$line'!\033[0m\n" 118 | diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp" 119 | exit 1 120 | fi 121 | done 122 | printf "\033[0;32mokay.\033[0m\n" 123 | ) 124 | done 125 | 126 | rm "$tmp" 127 | -------------------------------------------------------------------------------- /scripts/validate_naming.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift Tracing open source project 5 | ## 6 | ## Copyright (c) 2020 Moritz Lang and the Swift Tracing project authors 7 | ## Licensed under Apache License v2.0 8 | ## 9 | ## See LICENSE.txt for license information 10 | ## 11 | ## SPDX-License-Identifier: Apache-2.0 12 | ## 13 | ##===----------------------------------------------------------------------===## 14 | 15 | ##===----------------------------------------------------------------------===## 16 | ## 17 | ## This source file is part of the SwiftNIO open source project 18 | ## 19 | ## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors 20 | ## Licensed under Apache License v2.0 21 | ## 22 | ## See LICENSE.txt for license information 23 | ## See CONTRIBUTORS.txt for the list of SwiftNIO project authors 24 | ## 25 | ## SPDX-License-Identifier: Apache-2.0 26 | ## 27 | ##===----------------------------------------------------------------------===## 28 | 29 | printf "=> Checking for unacceptable language... " 30 | # This greps for unacceptable terminology. The square bracket[s] are so that 31 | # "git grep" doesn't find the lines that greps :). 32 | unacceptable_terms=( 33 | -e blacklis[t] 34 | -e whitelis[t] 35 | -e slav[e] 36 | ) 37 | if git grep --color=never -i "${unacceptable_terms[@]}" > /dev/null; then 38 | printf "\033[0;31mUnacceptable language found.\033[0m\n" 39 | git grep -i "${unacceptable_terms[@]}" 40 | exit 1 41 | fi 42 | printf "\033[0;32mokay.\033[0m\n" 43 | --------------------------------------------------------------------------------