Runtime context API (#948)
* runtime context API * fix comments * rename * changelog * changelog
This commit is contained in:
parent
d03dff2435
commit
3333957c12
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Introduced `RuntimeContext` API
|
||||
([#948](https://github.com/open-telemetry/opentelemetry-dotnet/pull/948))
|
||||
|
||||
## 0.4.0-beta.2
|
||||
|
||||
Released 2020-07-24
|
||||
|
||||
* First beta release
|
||||
|
||||
## 0.3.0-beta
|
||||
|
||||
Released 2020-07-23
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// <copyright file="AsyncLocalRuntimeContextSlot.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#if !NET452
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// The async local implementation of context slot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the underlying value.</typeparam>
|
||||
public class AsyncLocalRuntimeContextSlot<T> : RuntimeContextSlot<T>
|
||||
{
|
||||
private readonly AsyncLocal<T> slot;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AsyncLocalRuntimeContextSlot{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
public AsyncLocalRuntimeContextSlot(string name)
|
||||
: base(name)
|
||||
{
|
||||
this.slot = new AsyncLocal<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override T Get()
|
||||
{
|
||||
return this.slot.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Set(T value)
|
||||
{
|
||||
this.slot.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// <copyright file="RemotingRuntimeContextSlot.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#if NET452
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// The .NET Remoting implementation of context slot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the underlying value.</typeparam>
|
||||
public class RemotingRuntimeContextSlot<T> : RuntimeContextSlot<T>
|
||||
{
|
||||
// A special workaround to suppress context propagation cross AppDomains.
|
||||
//
|
||||
// By default the value added to System.Runtime.Remoting.Messaging.CallContext
|
||||
// will be marshalled/unmarshalled across AppDomain boundary. This will cause
|
||||
// serious issue if the destination AppDomain doesn't have the corresponding type
|
||||
// to unmarshal data.
|
||||
// The worst case is AppDomain crash with ReflectionLoadTypeException.
|
||||
//
|
||||
// The workaround is to use a well known type that exists in all AppDomains, and
|
||||
// put the actual payload as a non-public field so the field is ignored during
|
||||
// marshalling.
|
||||
private static readonly FieldInfo WrapperField = typeof(BitArray).GetField("_syncRoot", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RemotingRuntimeContextSlot{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
public RemotingRuntimeContextSlot(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override T Get()
|
||||
{
|
||||
var wrapper = CallContext.LogicalGetData(this.Name) as BitArray;
|
||||
|
||||
if (wrapper == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return (T)WrapperField.GetValue(wrapper);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Set(T value)
|
||||
{
|
||||
var wrapper = new BitArray(0);
|
||||
WrapperField.SetValue(wrapper, value);
|
||||
CallContext.LogicalSetData(this.Name, wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// <copyright file="RuntimeContext.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic runtime context management API.
|
||||
/// </summary>
|
||||
public sealed class RuntimeContext
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, object> Slots = new ConcurrentDictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actual context carrier implementation.
|
||||
/// </summary>
|
||||
#if !NET452
|
||||
public static Type ContextSlotType { get; set; } = typeof(AsyncLocalRuntimeContextSlot<>);
|
||||
#else
|
||||
public static Type ContextSlotType { get; set; } = typeof(RemotingRuntimeContextSlot<>);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Register a named context slot.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
/// <typeparam name="T">The type of the underlying value.</typeparam>
|
||||
public static void RegisterSlot<T>(string name)
|
||||
{
|
||||
lock (Slots)
|
||||
{
|
||||
if (Slots.ContainsKey(name))
|
||||
{
|
||||
throw new InvalidOperationException($"The context slot {name} is already registered.");
|
||||
}
|
||||
|
||||
var type = ContextSlotType.MakeGenericType(typeof(T));
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(string) });
|
||||
Slots[name] = ctor.Invoke(new object[] { name });
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static void Apply(IDictionary<string, object> snapshot)
|
||||
{
|
||||
foreach (var entry in snapshot)
|
||||
{
|
||||
// TODO: revisit this part if we want Snapshot() to be used on critical paths
|
||||
dynamic value = entry.Value;
|
||||
SetValue(entry.Key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static IDictionary<string, object> Snapshot()
|
||||
{
|
||||
var retval = new Dictionary<string, object>();
|
||||
foreach (var entry in Slots)
|
||||
{
|
||||
// TODO: revisit this part if we want Snapshot() to be used on critical paths
|
||||
dynamic slot = entry.Value;
|
||||
retval[entry.Key] = slot.Get();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value to a registered slot.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
/// <param name="value">The value to be set.</param>
|
||||
/// <typeparam name="T">The type of the value.</typeparam>
|
||||
public static void SetValue<T>(string name, T value)
|
||||
{
|
||||
var slot = (RuntimeContextSlot<T>)Slots[name];
|
||||
slot.Set(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value from a registered slot.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
/// <typeparam name="T">The type of the value.</typeparam>
|
||||
/// <returns>The value retrieved from the context slot.</returns>
|
||||
public static T GetValue<T>(string name)
|
||||
{
|
||||
var slot = (RuntimeContextSlot<T>)Slots[name];
|
||||
return slot.Get();
|
||||
}
|
||||
|
||||
// For testing purpose
|
||||
// private static Clear
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// <copyright file="RuntimeContextSlot.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// The abstract context slot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the underlying value.</typeparam>
|
||||
public abstract class RuntimeContextSlot<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RuntimeContextSlot{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
public RuntimeContextSlot(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the context slot.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value from the context slot.
|
||||
/// </summary>
|
||||
/// <returns>The value retrieved from the context slot.</returns>
|
||||
public abstract T Get();
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to the context slot.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to be set.</param>
|
||||
public abstract void Set(T value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// <copyright file="ThreadLocalRuntimeContextSlot.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenTelemetry.Context
|
||||
{
|
||||
/// <summary>
|
||||
/// The thread local (TLS) implementation of context slot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the underlying value.</typeparam>
|
||||
public class ThreadLocalRuntimeContextSlot<T> : RuntimeContextSlot<T>
|
||||
{
|
||||
private readonly ThreadLocal<T> slot;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ThreadLocalRuntimeContextSlot{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the context slot.</param>
|
||||
public ThreadLocalRuntimeContextSlot(string name)
|
||||
: base(name)
|
||||
{
|
||||
this.slot = new ThreadLocal<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override T Get()
|
||||
{
|
||||
return this.slot.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Set(T value)
|
||||
{
|
||||
this.slot.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue