diff --git a/csharp/src/Drivers/Databricks/DatabricksConnection.cs b/csharp/src/Drivers/Databricks/DatabricksConnection.cs index 14a0eef081..b0c2f88756 100644 --- a/csharp/src/Drivers/Databricks/DatabricksConnection.cs +++ b/csharp/src/Drivers/Databricks/DatabricksConnection.cs @@ -28,9 +28,15 @@ using Apache.Arrow.Adbc.Drivers.Apache.Hive2.Client; using Apache.Arrow.Adbc.Drivers.Apache.Spark; using Apache.Arrow.Adbc.Drivers.Databricks.Auth; +using Apache.Arrow.Adbc.Drivers.Databricks.CloudFetch; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model; +using Apache.Arrow.Adbc.Tracing; using Apache.Arrow.Ipc; using Apache.Hive.Service.Rpc.Thrift; using Thrift.Protocol; +using static Apache.Arrow.Adbc.Drivers.Databricks.Log.DatabricksLogger; namespace Apache.Arrow.Adbc.Drivers.Databricks { @@ -66,6 +72,11 @@ internal class DatabricksConnection : SparkHttpConnection private string _traceParentHeaderName = "traceparent"; private bool _traceStateEnabled = false; + // Telemetry + private TelemetryHelper? _telemetryHelper; + private DatabricksActivityListener _databricksActivityListener; + private Guid _guid; + // Identity federation client ID for token exchange private string? _identityFederationClientId; @@ -76,15 +87,16 @@ internal class DatabricksConnection : SparkHttpConnection public DatabricksConnection(IReadOnlyDictionary properties) : base(properties) { + _guid = Guid.NewGuid(); ValidateProperties(); + _databricksActivityListener = new DatabricksActivityListener(_telemetryHelper, this.AssemblyName, _guid); } public override IEnumerable>? GetActivitySourceTags(IReadOnlyDictionary properties) { IEnumerable>? tags = base.GetActivitySourceTags(properties); - // TODO: Add any additional tags specific to Databricks connection - //tags ??= []; - //tags.Concat([new("key", "value")]); + tags ??= []; + tags.Concat([new("guid",_guid)]); return tags; } @@ -103,6 +115,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.EnablePKFK}' value '{enablePKFKStr}' could not be parsed. Valid values are 'true', 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.EnablePKFK}' value '{enablePKFKStr}' could not be parsed. Valid values are 'true', 'false'."); } } @@ -115,6 +128,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.EnableMultipleCatalogSupport}' value '{enableMultipleCatalogSupportStr}' could not be parsed. Valid values are 'true', 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.EnableMultipleCatalogSupport}' value '{enableMultipleCatalogSupportStr}' could not be parsed. Valid values are 'true', 'false'."); } } @@ -127,6 +141,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.ApplySSPWithQueries}' value '{applySSPWithQueriesStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.ApplySSPWithQueries}' value '{applySSPWithQueriesStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -139,6 +154,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.EnableDirectResults}' value '{enableDirectResultsStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.EnableDirectResults}' value '{enableDirectResultsStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -152,6 +168,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.UseCloudFetch}' value '{useCloudFetchStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.UseCloudFetch}' value '{useCloudFetchStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -164,6 +181,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.CanDecompressLz4}' value '{canDecompressLz4Str}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.CanDecompressLz4}' value '{canDecompressLz4Str}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -176,6 +194,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.UseDescTableExtended}' value '{useDescTableExtendedStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.UseDescTableExtended}' value '{useDescTableExtendedStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -188,6 +207,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.EnableRunAsyncInThriftOp}' value '{enableRunAsyncInThriftStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.EnableRunAsyncInThriftOp}' value '{enableRunAsyncInThriftStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -196,11 +216,13 @@ private void ValidateProperties() { if (!long.TryParse(maxBytesPerFileStr, out long maxBytesPerFileValue)) { + LOGGER.error($"Parameter '{DatabricksParameters.MaxBytesPerFile}' value '{maxBytesPerFileStr}' could not be parsed. Valid values are positive integers."); throw new ArgumentException($"Parameter '{DatabricksParameters.MaxBytesPerFile}' value '{maxBytesPerFileStr}' could not be parsed. Valid values are positive integers."); } if (maxBytesPerFileValue <= 0) { + LOGGER.error($"Parameter '{DatabricksParameters.MaxBytesPerFile}' value must be a positive integer."); throw new ArgumentOutOfRangeException( nameof(Properties), maxBytesPerFileValue, @@ -243,6 +265,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.TracePropagationEnabled}' value '{tracePropagationEnabledStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.TracePropagationEnabled}' value '{tracePropagationEnabledStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -255,6 +278,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.TraceParentHeaderName}' cannot be empty."); throw new ArgumentException($"Parameter '{DatabricksParameters.TraceParentHeaderName}' cannot be empty."); } } @@ -267,6 +291,7 @@ private void ValidateProperties() } else { + LOGGER.error($"Parameter '{DatabricksParameters.TraceStateEnabled}' value '{traceStateEnabledStr}' could not be parsed. Valid values are 'true' and 'false'."); throw new ArgumentException($"Parameter '{DatabricksParameters.TraceStateEnabled}' value '{traceStateEnabledStr}' could not be parsed. Valid values are 'true' and 'false'."); } } @@ -281,6 +306,43 @@ private void ValidateProperties() { _identityFederationClientId = identityFederationClientId; } + + //Telemetry + var connectionParams = new DriverConnectionParameters(); + var hostDetails = new HostDetails(); + var clientContext = new ClientContext(); + string? token = null; + + if (Properties.TryGetValue(SparkParameters.AuthType, out string? authType)) + { + connectionParams.AuthMech = Util.StringToAuthMech(authType); + } + + if (Properties.TryGetValue(SparkParameters.HostName, out string? host)) + { + hostDetails.HostUrl = host; + } + if (Properties.TryGetValue(SparkParameters.Port, out string? port)) + { + hostDetails.Port = Int32.Parse(port); + } + connectionParams.HostInfo = hostDetails; + + if (Properties.TryGetValue(SparkParameters.UserAgentEntry, out string? userAgent)) + { + clientContext.UserAgent = userAgent; + } + + if(Properties.TryGetValue(SparkParameters.AccessToken, out string? accessToken)) + { + token = accessToken; + } + else if(Properties.TryGetValue(SparkParameters.Token, out string? accesstoken)) + { + token = accesstoken; + } + _telemetryHelper = new TelemetryHelper(hostDetails.HostUrl, token); + _telemetryHelper.SetParameters(connectionParams, clientContext); } /// @@ -407,6 +469,7 @@ protected override HttpMessageHandler CreateHttpHandler() { if (string.IsNullOrEmpty(accessToken)) { + LOGGER.error("Access token is required for OAuth authentication with token renewal"); throw new ArgumentException("Access token is required for OAuth authentication with token renewal."); } @@ -423,6 +486,7 @@ protected override HttpMessageHandler CreateHttpHandler() } } + _telemetryHelper?.InitializeTelemetryClient(_authHttpClient); return baseHandler; } @@ -506,6 +570,7 @@ protected override async Task HandleOpenSessionResponse(TOpenSessionResp? sessio { var version = session.ServerProtocolVersion; if (!FeatureVersionNegotiator.IsDatabricksProtocolVersion(version)) { + LOGGER.error($"Attempted to use databricks driver with a non-databricks server"); throw new DatabricksException("Attempted to use databricks driver with a non-databricks server"); } _enablePKFK = _enablePKFK && FeatureVersionNegotiator.SupportsPKFK(version); @@ -610,6 +675,7 @@ protected override void ValidateOptions() { if (!bool.TryParse(tempUnavailableRetryStr, out bool tempUnavailableRetryValue)) { + LOGGER.error($"Failed to parse parameter '{DatabricksParameters.TemporarilyUnavailableRetry}' with value '{tempUnavailableRetryStr}'. Must be a value of false (disabled) or true (enabled)."); throw new ArgumentOutOfRangeException(DatabricksParameters.TemporarilyUnavailableRetry, tempUnavailableRetryStr, $"must be a value of false (disabled) or true (enabled). Default is true."); } @@ -623,6 +689,7 @@ protected override void ValidateOptions() if (!int.TryParse(tempUnavailableRetryTimeoutStr, out int tempUnavailableRetryTimeoutValue) || tempUnavailableRetryTimeoutValue < 0) { + LOGGER.error($"Failed to parse parameter '{DatabricksParameters.TemporarilyUnavailableRetryTimeout}' with value '{tempUnavailableRetryTimeoutStr}'. Must be a value of 0 (retry indefinitely) or a positive integer representing seconds."); throw new ArgumentOutOfRangeException(DatabricksParameters.TemporarilyUnavailableRetryTimeout, tempUnavailableRetryTimeoutStr, $"must be a value of 0 (retry indefinitely) or a positive integer representing seconds. Default is 900 seconds (15 minutes)."); } @@ -683,6 +750,7 @@ protected override void ValidateOAuthParameters() if (!DatabricksOAuthGrantTypeParser.TryParse(grantTypeStr, out grantType)) { + LOGGER.error($"Unsupported {DatabricksParameters.OAuthGrantType} value. Refer to the Databricks documentation for valid values."); throw new ArgumentOutOfRangeException( DatabricksParameters.OAuthGrantType, grantTypeStr, @@ -698,12 +766,14 @@ protected override void ValidateOAuthParameters() if (string.IsNullOrEmpty(clientId)) { + LOGGER.error($"Parameter '{DatabricksParameters.OAuthGrantType}' is set to '{DatabricksConstants.OAuthGrantTypes.ClientCredentials}' but parameter '{DatabricksParameters.OAuthClientId}' is not set."); throw new ArgumentException( $"Parameter '{DatabricksParameters.OAuthGrantType}' is set to '{DatabricksConstants.OAuthGrantTypes.ClientCredentials}' but parameter '{DatabricksParameters.OAuthClientId}' is not set. Please provide a value for '{DatabricksParameters.OAuthClientId}'.", nameof(Properties)); } if (string.IsNullOrEmpty(clientSecret)) { + LOGGER.error($"Parameter '{DatabricksParameters.OAuthGrantType}' is set to '{DatabricksConstants.OAuthGrantTypes.ClientCredentials}' but parameter '{DatabricksParameters.OAuthClientSecret}' is not set."); throw new ArgumentException( $"Parameter '{DatabricksParameters.OAuthGrantType}' is set to '{DatabricksConstants.OAuthGrantTypes.ClientCredentials}' but parameter '{DatabricksParameters.OAuthClientSecret}' is not set. Please provide a value for '{DatabricksParameters.OAuthClientSecret}'.", nameof(Properties)); @@ -736,6 +806,7 @@ private string GetHost() } } + LOGGER.error("Host not found in connection properties. Please provide a valid host using either 'HostName' or 'Uri' property."); throw new ArgumentException("Host not found in connection properties. Please provide a valid host using either 'HostName' or 'Uri' property."); } @@ -757,6 +828,16 @@ protected override void Dispose(bool disposing) if (disposing) { _authHttpClient?.Dispose(); + _databricksActivityListener?.Dispose(); + + try + { + _telemetryHelper?.ForceFlushAsync().Wait(TimeSpan.FromSeconds(2)); + } + catch (Exception ex) + { + LOGGER.error($"Failed to flush telemetry on dispose: {ex.Message}"); + } } base.Dispose(disposing); } diff --git a/csharp/src/Drivers/Databricks/Log/DatabricksLogger.cs b/csharp/src/Drivers/Databricks/Log/DatabricksLogger.cs new file mode 100644 index 0000000000..2225f8b45f --- /dev/null +++ b/csharp/src/Drivers/Databricks/Log/DatabricksLogger.cs @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +using System; +using System.IO; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Log; + +public class DatabricksLogger +{ + public static readonly string DATABRICKS_LOG_FILE = "databricks_adbc.log"; + public static readonly DatabricksLogger LOGGER = new DatabricksLogger(); + + private static readonly object _logLock = new object(); + private static readonly string _logFilePath = Path.Combine(Path.GetTempPath(), DATABRICKS_LOG_FILE); + + public void trace(string message) + { + WriteLog("TRACE", message); + } + + public void debug(string message) + { + WriteLog("DEBUG", message); + } + + public void info(string message) + { + WriteLog("INFO", message); + } + + public void warn(string message) + { + WriteLog("WARN", message); + } + + public void error(string message) + { + WriteLog("ERROR", message); + } + + public void fatal(string message) + { + WriteLog("FATAL", message); + } + + private static void WriteLog(string level, string message) + { + var logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}"; + + lock (_logLock) + { + try + { + File.AppendAllText(_logFilePath, logMessage + Environment.NewLine); + } + catch (Exception) + { + } + } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/DatabricksActivityListener.cs b/csharp/src/Drivers/Databricks/Telemetry/DatabricksActivityListener.cs new file mode 100644 index 0000000000..7b84cd20f8 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/DatabricksActivityListener.cs @@ -0,0 +1,70 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Apache.Arrow.Adbc.Tracing; +using Apache.Arrow.Adbc.Drivers.Apache; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; +using static Apache.Arrow.Adbc.Drivers.Databricks.Log.DatabricksLogger; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry +{ + internal class DatabricksActivityListener : IDisposable + { + private readonly ActivityListener _activityListener; + private TelemetryHelper? _telemetryHelper; + + public DatabricksActivityListener(TelemetryHelper? telemetryHelper, string sourceName, Guid guid) + { + this._telemetryHelper = telemetryHelper; + this._activityListener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Tags?.Any(kvp => kvp.Key == "guid" && kvp.Value?.Equals(guid) == true) == true, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData, + ActivityStopped = OnActivityStopped, + }; + ActivitySource.AddActivityListener(_activityListener); + } + + private void OnActivityStopped(Activity activity) + { + if(_telemetryHelper == null) + { + return; + } + + if(activity.OperationName?.EndsWith("ExecuteStatementAsync") == true) + { + LOGGER.info("OnActivityStopped: " + activity.OperationName); + var sqlExecutionEvent = new SqlExecutionEvent(); + var operationDetail = new OperationDetail(); + operationDetail.OperationType = Util.StringToOperationType("EXECUTE_STATEMENT_ASYNC"); + sqlExecutionEvent.OperationDetail = operationDetail; + _telemetryHelper.AddSqlExecutionEvent(sqlExecutionEvent, Convert.ToInt64(activity.Duration.TotalMilliseconds)); + } + } + + public void Dispose() + { + this._activityListener.Dispose(); + } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/DatabricksConnectionConfig.cs b/csharp/src/Drivers/Databricks/Telemetry/DatabricksConnectionConfig.cs new file mode 100644 index 0000000000..0530e09a41 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/DatabricksConnectionConfig.cs @@ -0,0 +1,24 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry; + +internal class DatabricksConnectionConfig +{ + public static readonly int MAX_BATCH_SIZE = 200; + public static readonly int FLUSH_INTERVAL_MILLIS = 300000; // 5 minutes +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/README.md b/csharp/src/Drivers/Databricks/Telemetry/README.md new file mode 100644 index 0000000000..b0949b8444 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/README.md @@ -0,0 +1,7 @@ +# Databricks Telemetry + +Uses `DatabricksActivityListener.cs` to listen and record events to `TelemetryHelper.cs` using ActivityListener. + +`TelemetryHelper.cs` stores telemetry logs and telemetry parameters. + +`TelemtryClient.cs` sends logs to `/telemetry` or `/telemetry-unauth` depending on authentication level. diff --git a/csharp/src/Drivers/Databricks/Telemetry/TelemetryClient.cs b/csharp/src/Drivers/Databricks/Telemetry/TelemetryClient.cs new file mode 100644 index 0000000000..ede22f6871 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/TelemetryClient.cs @@ -0,0 +1,124 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model; +using static Apache.Arrow.Adbc.Drivers.Databricks.Log.DatabricksLogger; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry +{ + internal class TelemetryClient + { + private readonly HttpClient _httpClient; + private readonly string? _telemetryUrl; + private readonly string? _accessToken; + + public TelemetryClient(HttpClient httpClient, string? hostUrl, string? accessToken) + { + _httpClient = httpClient; + _accessToken = accessToken; + _telemetryUrl = !string.IsNullOrEmpty(hostUrl) ? accessToken != null ? $"https://{hostUrl}/telemetry-ext" : $"https://{hostUrl}/telemetry-unauth" : null; + } + + /// + /// Sends a batch of telemetry events asynchronously + /// + /// List of telemetry events to send + /// Task representing the async operation + public async Task SendTelemetryBatchAsync(List telemetryBatch) + { + if (string.IsNullOrEmpty(_telemetryUrl) || telemetryBatch.Count == 0) + { + return false; + } + + try + { + var request = new HttpRequestMessage(HttpMethod.Post, _telemetryUrl); + + // Serialize the batch to JSON + var telemetryRequest = new TelemetryRequest(); + telemetryRequest.UploadTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + telemetryRequest.ProtoLogs = telemetryBatch.Select(x => JsonSerializer.Serialize(x)).ToList(); + request.Content = new StringContent(JsonSerializer.Serialize(telemetryRequest)); + + // Set headers + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + if(_accessToken != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken); + } + + var response = await _httpClient.SendAsync(request); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + LOGGER.error($"Failed to send telemetry: {ex.Message}"); + return false; + } + } + + /// + /// Sends a single telemetry event asynchronously + /// + /// Single telemetry event to send + /// Task representing the async operation + public async Task SendTelemetryAsync(TelemetryFrontendLog telemetryEvent) + { + if (string.IsNullOrEmpty(_telemetryUrl)) + { + return false; + } + + try + { + var request = new HttpRequestMessage(HttpMethod.Post, _telemetryUrl); + + // Serialize the event to JSON + var telemetryRequest = new TelemetryRequest(); + telemetryRequest.UploadTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + telemetryRequest.ProtoLogs = new List { JsonSerializer.Serialize(telemetryEvent) }; + request.Content = new StringContent(JsonSerializer.Serialize(telemetryRequest)); + + // Set headers + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + if(_accessToken != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken); + } + + var response = await _httpClient.SendAsync(request); + LOGGER.info($"Telemetry response: {response.StatusCode}"); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + LOGGER.error($"Failed to send telemetry: {ex.Message}"); + return false; + } + } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/TelemetryHelper.cs b/csharp/src/Drivers/Databricks/Telemetry/TelemetryHelper.cs new file mode 100644 index 0000000000..40f8a0614a --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/TelemetryHelper.cs @@ -0,0 +1,176 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Concurrent; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; +using static Apache.Arrow.Adbc.Drivers.Databricks.Log.DatabricksLogger; +using Apache.Arrow.Adbc.Drivers.Apache.Spark; +using System.Text.Json; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry +{ + internal class TelemetryHelper + { + private static readonly ConcurrentQueue EventsBatch = new ConcurrentQueue(); + private long _lastFlushTimeMillis; + private readonly Timer _flushTimer; + + private TelemetryClient? _telemetryClient; + + private ClientContext? _clientContext; + private string? _accessToken; + private string? _hostUrl; + private DriverConnectionParameters? _connectionParameters; + private readonly DriverSystemConfiguration _systemConfiguration; + + public TelemetryHelper(string? hostUrl, string? accessToken) + { + _hostUrl = hostUrl; + _accessToken = accessToken; + _lastFlushTimeMillis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + _flushTimer = new Timer(TimerFlushEvents, null, DatabricksConnectionConfig.FLUSH_INTERVAL_MILLIS, DatabricksConnectionConfig.FLUSH_INTERVAL_MILLIS); + _systemConfiguration = new DriverSystemConfiguration() + { + DriverVersion = Util.GetDriverVersion(), + DriverName = Util.GetDriverName(), + OsName = Environment.OSVersion.Platform.ToString(), + OsVersion = Environment.OSVersion.Version.ToString(), + OsArch = Environment.OSVersion.Platform.ToString(), + RuntimeName = Assembly.GetExecutingAssembly().GetName().Name, + RuntimeVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(), + RuntimeVendor = RuntimeInformation.FrameworkDescription, + ClientAppName = null, + LocaleName = CultureInfo.CurrentCulture.Name, + ProcessName = Process.GetCurrentProcess().ProcessName + }; + } + + public void SetParameters(DriverConnectionParameters connectionParameters, ClientContext clientContext) + { + _connectionParameters = connectionParameters; + _clientContext = clientContext; + } + + public void InitializeTelemetryClient(HttpClient? httpClient) + { + if (httpClient == null) + { + // Should not happen, but we should log if it does + LOGGER.error("TelemetryHelper.InitializeTelemetryClient: httpClient is null"); + return; + } + + if (_telemetryClient == null && _connectionParameters?.HostInfo != null) + { + _telemetryClient = new TelemetryClient(httpClient, _connectionParameters.HostInfo.HostUrl, _accessToken); + } + } + + public void AddSqlExecutionEvent(SqlExecutionEvent sqlExecutionEvent, long? latencyMs) + { + var telemetryEvent = new TelemetryEvent(); + var telemetryFrontendLog = new TelemetryFrontendLog(); + var frontendLogEntry = new FrontendLogEntry(); + var frontendLogContext = new FrontendLogContext(); + + telemetryEvent.DriverConnectionParameters = _connectionParameters; + telemetryEvent.SystemConfiguration = _systemConfiguration; + telemetryEvent.SqlExecutionEvent = sqlExecutionEvent; + telemetryEvent.LatencyMs = latencyMs; + frontendLogContext.ClientContext = _clientContext; + frontendLogEntry.SqlDriverLog = telemetryEvent; + telemetryFrontendLog.Entry = frontendLogEntry; + telemetryFrontendLog.Context = frontendLogContext; + + EventsBatch.Enqueue(telemetryFrontendLog); + + if (EventsBatch.Count >= DatabricksConnectionConfig.MAX_BATCH_SIZE) + { + Task.Run(() => FlushEvents()); + } + } + + private void TimerFlushEvents(object? state) + { + var currentTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + if (currentTime - _lastFlushTimeMillis >= DatabricksConnectionConfig.FLUSH_INTERVAL_MILLIS) + { + Task.Run(() => FlushEvents()); + } + } + + private async Task FlushEvents() + { + if (_telemetryClient == null) + { + return; + } + + var eventsToFlush = new List(); + + // Dequeue all current events + while (EventsBatch.TryDequeue(out var telemetryEvent)) + { + eventsToFlush.Add(telemetryEvent); + } + + if (eventsToFlush.Count == 0) + { + return; + } + + _lastFlushTimeMillis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + try + { + var success = await _telemetryClient.SendTelemetryBatchAsync(eventsToFlush); + + if (!success) + { + System.Diagnostics.Debug.WriteLine("Failed to send telemetry batch"); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Exception while flushing telemetry events: {ex.Message}"); + } + } + + public async Task ForceFlushAsync() + { + await FlushEvents(); + } + + public void Dispose() + { + _flushTimer?.Dispose(); + + Task.Run(async () => await FlushEvents()).Wait(TimeSpan.FromSeconds(5)); + } + } +} \ No newline at end of file diff --git a/csharp/src/Drivers/Databricks/Telemetry/Util.cs b/csharp/src/Drivers/Databricks/Telemetry/Util.cs new file mode 100644 index 0000000000..952214b4c6 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/Util.cs @@ -0,0 +1,107 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System; +using System.Diagnostics; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry +{ + internal class Util + { + private static string DRIVER_VERSION = GetAssemblyVersion(typeof(DatabricksConnection)); + + private static string DRIVER_NAME = GetAssemblyName(typeof(DatabricksConnection)); + + private static string GetAssemblyName(Type type) => type.Assembly.GetName().Name!; + + private static string GetAssemblyVersion(Type type) => FileVersionInfo.GetVersionInfo(type.Assembly.Location).ProductVersion ?? string.Empty; + + + public static string GetDriverVersion() + { + return DRIVER_VERSION; + } + + public static string GetDriverName() + { + return DRIVER_NAME; + } + + public static AuthMech StringToAuthMech(String authType) + { + switch (authType) + { + case "none": + return AuthMech.OTHER; + case "basic": + return AuthMech.OTHER; + case "token": + return AuthMech.PAT; + case "oauth": + return AuthMech.OAUTH; + default: + return AuthMech.TYPE_UNSPECIFIED; + } + } + + public static OperationType StringToOperationType(String? operationType) + { + switch (operationType) + { + case "TYPE_UNSPECIFIED": + return OperationType.TYPE_UNSPECIFIED; + case "CREATE_SESSION": + return OperationType.CREATE_SESSION; + case "DELETE_SESSION": + return OperationType.DELETE_SESSION; + case "EXECUTE_STATEMENT": + return OperationType.EXECUTE_STATEMENT; + case "EXECUTE_STATEMENT_ASYNC": + return OperationType.EXECUTE_STATEMENT_ASYNC; + case "CLOSE_STATEMENT": + return OperationType.CLOSE_STATEMENT; + case "CANCEL_STATEMENT": + return OperationType.CANCEL_STATEMENT; + case "LIST_TYPE_INFO": + return OperationType.LIST_TYPE_INFO; + case "LIST_CATALOGS": + return OperationType.LIST_CATALOGS; + case "LIST_SCHEMAS": + return OperationType.LIST_SCHEMAS; + case "LIST_TABLES": + return OperationType.LIST_TABLES; + case "LIST_TABLE_TYPES": + return OperationType.LIST_TABLE_TYPES; + case "LIST_COLUMNS": + return OperationType.LIST_COLUMNS; + case "LIST_FUNCTIONS": + return OperationType.LIST_FUNCTIONS; + case "LIST_PRIMARY_KEYS": + return OperationType.LIST_PRIMARY_KEYS; + case "LIST_IMPORTED_KEYS": + return OperationType.LIST_IMPORTED_KEYS; + case "LIST_EXPORTED_KEYS": + return OperationType.LIST_EXPORTED_KEYS; + case "LIST_CROSS_REFERENCES": + return OperationType.LIST_CROSS_REFERENCES; + default: + return OperationType.TYPE_UNSPECIFIED; + } + } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/AuthFlow.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/AuthFlow.cs new file mode 100644 index 0000000000..04a961cee9 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/AuthFlow.cs @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum AuthFlow + { + TYPE_UNSPECIFIED = 0, + TOKEN_PASSTHROUGH = 1, + CLIENT_CREDENTIALS = 2, + BROWSER_BASED_AUTHENTICATION = 3 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/AuthMech.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/AuthMech.cs new file mode 100644 index 0000000000..34152a6c21 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/AuthMech.cs @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum AuthMech + { + TYPE_UNSPECIFIED = 0, + OTHER = 1, + PAT = 2, + OAUTH = 3 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/DriverProxy.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/DriverProxy.cs new file mode 100644 index 0000000000..aef79ba557 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/DriverProxy.cs @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum DriverProxy + { + AUTH_UNSPECIFIED = 0, + NONE = 1, + BASIC = 2, + SPNEGO = 3 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/DriverVolumeOperationType.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/DriverVolumeOperationType.cs new file mode 100644 index 0000000000..3e231a3a76 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/DriverVolumeOperationType.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum DriverVolumeOperationType + { + TYPE_UNSPECIFIED = 0, + PUT = 1, + GET = 2, + DELETE = 3, + LIST = 4, + QUERY = 5 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/ExecutionResultFormat.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/ExecutionResultFormat.cs new file mode 100644 index 0000000000..92be5d5b55 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/ExecutionResultFormat.cs @@ -0,0 +1,28 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum ExecutionResultFormat + { + FORMAT_UNSPECIFIED = 0, + INLINE_ARROW = 1, + INLINE_JSON = 2, + EXTERNAL_LINKS = 3, + COLUMNAR_INLINE = 4 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/OperationType.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/OperationType.cs new file mode 100644 index 0000000000..74b4d6ad20 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/OperationType.cs @@ -0,0 +1,41 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum OperationType + { + TYPE_UNSPECIFIED = 0, + CREATE_SESSION = 1, + DELETE_SESSION = 2, + EXECUTE_STATEMENT = 3, + EXECUTE_STATEMENT_ASYNC = 4, + CLOSE_STATEMENT = 5, + CANCEL_STATEMENT = 6, + LIST_TYPE_INFO = 7, + LIST_CATALOGS = 8, + LIST_SCHEMAS = 9, + LIST_TABLES = 10, + LIST_TABLE_TYPES = 11, + LIST_COLUMNS = 12, + LIST_FUNCTIONS = 13, + LIST_PRIMARY_KEYS = 14, + LIST_IMPORTED_KEYS = 15, + LIST_EXPORTED_KEYS = 16, + LIST_CROSS_REFERENCES = 17 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/enums/StatementType.cs b/csharp/src/Drivers/Databricks/Telemetry/enums/StatementType.cs new file mode 100644 index 0000000000..482f84cac9 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/enums/StatementType.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums +{ + internal enum StatementType + { + TYPE_UNSPECIFIED = 0, + QUERY = 1, + SQL = 2, + UPDATE = 3, + METADATA = 4, + VOLUME = 5 + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/ChunkDetails.cs b/csharp/src/Drivers/Databricks/Telemetry/model/ChunkDetails.cs new file mode 100644 index 0000000000..7d7932c5fd --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/ChunkDetails.cs @@ -0,0 +1,46 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class ChunkDetails + { + [JsonPropertyName("initial_chunk_latency_millis")] + public long InitialChunkLatencyMillis { get; set; } + + [JsonPropertyName("slowest_chunk_latency_millis")] + public long SlowestChunkLatencyMillis { get; set; } + + [JsonPropertyName("total_chunks_present")] + public long TotalChunksPresent { get; set; } + + [JsonPropertyName("total_chunks_iterated")] + public long TotalChunksIterated { get; set; } + + [JsonPropertyName("sum_chunks_download_time_millis")] + public long SumChunksDownloadTimeMillis { get; set; } + + public ChunkDetails(long totalChunks) + { + this.TotalChunksPresent = totalChunks; + this.TotalChunksIterated = 0; + this.SumChunksDownloadTimeMillis = 0; + } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/ClientContext.cs b/csharp/src/Drivers/Databricks/Telemetry/model/ClientContext.cs new file mode 100644 index 0000000000..a4fa6d0418 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/ClientContext.cs @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class ClientContext + { + [JsonPropertyName("timestamp_millis")] + public long TimestampMillis { get; set; } + + [JsonPropertyName("user_agent")] + public string? UserAgent { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/DriverConnectionParameters.cs b/csharp/src/Drivers/Databricks/Telemetry/model/DriverConnectionParameters.cs new file mode 100644 index 0000000000..19465f4e42 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/DriverConnectionParameters.cs @@ -0,0 +1,148 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class DriverConnectionParameters + { + [JsonPropertyName("http_path")] + public string? HTTPPath { get; set; } + + [JsonPropertyName("mode")] + public string? DriverMode { get; set; } + + [JsonPropertyName("host_info")] + public HostDetails? HostInfo { get; set; } + + [JsonPropertyName("use_proxy")] + public bool UseProxy { get; set; } + + [JsonPropertyName("auth_mech")] + public AuthMech AuthMech { get; set; } + + [JsonPropertyName("auth_flow")] + public AuthFlow AuthFlow { get; set; } + + [JsonPropertyName("auth_scope")] + public string? AuthScope { get; set; } + + [JsonPropertyName("use_system_proxy")] + public bool UseSystemProxy { get; set; } + + [JsonPropertyName("non_proxy_hosts")] + public string[]? NonProxyHosts { get; set; } + + [JsonPropertyName("use_cf_proxy")] + public bool UseCfProxy { get; set; } + + [JsonPropertyName("proxy_host_info")] + public HostDetails? ProxyHostInfo { get; set; } + + [JsonPropertyName("cf_proxy_host_info")] + public HostDetails? CfProxyHostInfo { get; set; } + + [JsonPropertyName("discovery_mode_enabled")] + public bool DiscoveryModeEnabled { get; set; } + + [JsonPropertyName("discovery_url")] + public string? DiscoveryUrl { get; set; } + + [JsonPropertyName("use_empty_metadata")] + public bool UseEmptyMetadata { get; set; } + + [JsonPropertyName("support_many_parameters")] + public bool SupportManyParameters { get; set; } + + [JsonPropertyName("ssl_trust_store_type")] + public string? SslTrustStoreType { get; set; } + + [JsonPropertyName("check_certificate_revocation")] + public bool CheckCertificateRevocation { get; set; } + + [JsonPropertyName("accept_undetermined_certificate_revocation")] + public bool AcceptUndeterminedCertificateRevocation { get; set; } + + [JsonPropertyName("google_service_account")] + public string? GoogleServiceAccount { get; set; } + + [JsonPropertyName("google_credential_file_path")] + public string? GoogleCredentialFilePath { get; set; } + + [JsonPropertyName("http_connection_pool_size")] + public long HttpConnectionPoolSize { get; set; } + + [JsonPropertyName("enable_sea_hybrid_results")] + public bool EnableSeaHybridResults { get; set; } + + [JsonPropertyName("enable_complex_datatype_support")] + public bool EnableComplexDatatypeSupport { get; set; } + + [JsonPropertyName("allow_self_signed_support")] + public bool AllowSelfSignedSupport { get; set; } + + [JsonPropertyName("use_system_trust_store")] + public bool UseSystemTrustStore { get; set; } + + [JsonPropertyName("rows_fetched_per_block")] + public long RowsFetchedPerBlock { get; set; } + + [JsonPropertyName("azure_workspace_resource_id")] + public bool AzureWorkspaceResourceId { get; set; } + + [JsonPropertyName("azure_tenant_id")] + public string? AzureTenantId { get; set; } + + [JsonPropertyName("string_column_length")] + public int StringColumnLength { get; set; } + + [JsonPropertyName("enable_token_cache")] + public bool EnableTokenCache { get; set; } + + [JsonPropertyName("token_endpoint")] + public string? TokenEndpoint { get; set; } + + [JsonPropertyName("auth_endpoint")] + public string? AuthEndpoint { get; set; } + + [JsonPropertyName("enable_arrow")] + public bool EnableArrow { get; set; } + + [JsonPropertyName("enable_direct_results")] + public bool EnableDirectResults { get; set; } + + [JsonPropertyName("enable_jwt_assertion")] + public bool EnableJwtAssertion { get; set; } + + [JsonPropertyName("jwt_key_file")] + public bool JwtKeyFile { get; set; } + + [JsonPropertyName("jwt_algorithm")] + public string? JwtAlgorithm { get; set; } + + [JsonPropertyName("socket_timeout")] + public long SocketTimeout { get; set; } + + [JsonPropertyName("allowed_volume_ingestion_paths")] + public long AllowedVolumeIngestionPaths { get; set; } + + [JsonPropertyName("async_poll_interval_millis")] + public long AsyncPollIntervalMillis { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/DriverErrorInfo.cs b/csharp/src/Drivers/Databricks/Telemetry/model/DriverErrorInfo.cs new file mode 100644 index 0000000000..c1d94e5b05 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/DriverErrorInfo.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class DriverErrorInfo + { + [JsonPropertyName("error_name")] + public string? ErrorName { get; set; } + + [JsonPropertyName("stack_trace")] + public string? StackTrace { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/DriverSystemConfiguration.cs b/csharp/src/Drivers/Databricks/Telemetry/model/DriverSystemConfiguration.cs new file mode 100644 index 0000000000..ef69019eec --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/DriverSystemConfiguration.cs @@ -0,0 +1,60 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class DriverSystemConfiguration + { + [JsonPropertyName("driver_version")] + public string? DriverVersion { get; set; } + + [JsonPropertyName("os_name")] + public string? OsName { get; set; } + + [JsonPropertyName("os_version")] + public string? OsVersion { get; set; } + + [JsonPropertyName("os_arch")] + public string? OsArch { get; set; } + + [JsonPropertyName("runtime_name")] + public string? RuntimeName { get; set; } + + [JsonPropertyName("runtime_version")] + public string? RuntimeVersion { get; set; } + + [JsonPropertyName("runtime_vendor")] + public string? RuntimeVendor { get; set; } + + [JsonPropertyName("client_app_name")] + public string? ClientAppName { get; set; } + + [JsonPropertyName("locale_name")] + public string? LocaleName { get; set; } + + [JsonPropertyName("driver_name")] + public string? DriverName { get; set; } + + [JsonPropertyName("char_set_encoding")] + public string? CharSetEncoding { get; set; } + + [JsonPropertyName("process_name")] + public string? ProcessName { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/DriverVolumeOperation.cs b/csharp/src/Drivers/Databricks/Telemetry/model/DriverVolumeOperation.cs new file mode 100644 index 0000000000..8d83343fb5 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/DriverVolumeOperation.cs @@ -0,0 +1,31 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class DriverVolumeOperation + { + [JsonPropertyName("operation_type")] + public DriverVolumeOperationType OperationType { get; set; } + + [JsonPropertyName("volume_path")] + public string? VolumePath { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogContext.cs b/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogContext.cs new file mode 100644 index 0000000000..bd98238233 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogContext.cs @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class FrontendLogContext + { + [JsonPropertyName("client_context")] + public ClientContext? ClientContext { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogEntry.cs b/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogEntry.cs new file mode 100644 index 0000000000..c4d827fb3f --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/FrontendLogEntry.cs @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class FrontendLogEntry + { + [JsonPropertyName("sql_driver_log")] + public TelemetryEvent? SqlDriverLog { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/HostDetails.cs b/csharp/src/Drivers/Databricks/Telemetry/model/HostDetails.cs new file mode 100644 index 0000000000..3c958a97b5 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/HostDetails.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class HostDetails + { + [JsonPropertyName("host_url")] + public string? HostUrl { get; set; } + + [JsonPropertyName("port")] + public int Port { get; set; } + + [JsonPropertyName("proxy_auth_type")] + public DriverProxy proxyType { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/OperationDetail.cs b/csharp/src/Drivers/Databricks/Telemetry/model/OperationDetail.cs new file mode 100644 index 0000000000..6e86be34a0 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/OperationDetail.cs @@ -0,0 +1,37 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class OperationDetail + { + [JsonPropertyName("n_operation_status_calls")] + public int NOperationStatusCalls { get; set; } + + [JsonPropertyName("operation_status_latency_millis")] + public long OperationStatusLatencyMillis { get; set; } + + [JsonPropertyName("operation_type")] + public OperationType OperationType { get; set; } + + [JsonPropertyName("is_internal_call")] + public bool IsInternalCall { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/ResultLatency.cs b/csharp/src/Drivers/Databricks/Telemetry/model/ResultLatency.cs new file mode 100644 index 0000000000..22dc5874ce --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/ResultLatency.cs @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class ResultLatency + { + [JsonPropertyName("result_set_ready_latency_millis")] + public long ResultSetReadyLatencyMillis { get; set; } + + [JsonPropertyName("result_set_consumption_latency_millis")] + public long ResultSetConsumptionLatencyMillis { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/SqlExecutionEvent.cs b/csharp/src/Drivers/Databricks/Telemetry/model/SqlExecutionEvent.cs new file mode 100644 index 0000000000..8cf22ff7a0 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/SqlExecutionEvent.cs @@ -0,0 +1,49 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Enums; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class SqlExecutionEvent + { + [JsonPropertyName("statement_type")] + public StatementType StatementType { get; set; } + + [JsonPropertyName("is_compressed")] + public bool IsCompressed { get; set; } + + [JsonPropertyName("execution_result")] + public ExecutionResultFormat ExecutionResult { get; set; } + + [JsonPropertyName("chunk_id")] + public long ChunkId { get; set; } + + [JsonPropertyName("retry_count")] + public long RetryCount { get; set; } + + [JsonPropertyName("chunk_details")] + public ChunkDetails? ChunkDetails { get; set; } + + [JsonPropertyName("result_latency")] + public ResultLatency? ResultLatency { get; set; } + + [JsonPropertyName("operation_detail")] + public OperationDetail? OperationDetail { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryEvent.cs b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryEvent.cs new file mode 100644 index 0000000000..dc87137495 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryEvent.cs @@ -0,0 +1,51 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class TelemetryEvent + { + [JsonPropertyName("session_id")] + public string? SessionId { get; set; } + + [JsonPropertyName("sql_statement_id")] + public string? SqlStatementId { get; set; } + + [JsonPropertyName("system_configuration")] + public DriverSystemConfiguration? SystemConfiguration { get; set; } + + [JsonPropertyName("driver_connection_params")] + public DriverConnectionParameters? DriverConnectionParameters { get; set; } + + [JsonPropertyName("auth_type")] + public string? AuthType { get; set; } + + [JsonPropertyName("vol_operation")] + public DriverVolumeOperation? VolumeOperation { get; set; } + + [JsonPropertyName("sql_operation")] + public SqlExecutionEvent? SqlExecutionEvent { get; set; } + + [JsonPropertyName("error_info")] + public DriverErrorInfo? ErrorInfo { get; set; } + + [JsonPropertyName("operation_latency_ms")] + public long? LatencyMs { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryFrontendLog.cs b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryFrontendLog.cs new file mode 100644 index 0000000000..965ee48c12 --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryFrontendLog.cs @@ -0,0 +1,36 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class TelemetryFrontendLog + { + [JsonPropertyName("workspace_id")] + public long WorkspaceId { get; set; } + + [JsonPropertyName("frontend_log_event_id")] + public string? FrontendLogEventId { get; set; } + + [JsonPropertyName("entry")] + public FrontendLogEntry? Entry { get; set; } + + [JsonPropertyName("context")] + public FrontendLogContext? Context { get; set; } + } +} diff --git a/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryRequest.cs b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryRequest.cs new file mode 100644 index 0000000000..0631df4c1d --- /dev/null +++ b/csharp/src/Drivers/Databricks/Telemetry/model/TelemetryRequest.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +using System.Text.Json.Serialization; +using System.Collections.Generic; + +namespace Apache.Arrow.Adbc.Drivers.Databricks.Telemetry.Model +{ + internal class TelemetryRequest + { + [JsonPropertyName("upload_time")] + public long UploadTime { get; set; } + + [JsonPropertyName("items")] + public List Items = new List(); + + [JsonPropertyName("protoLogs")] + public List? ProtoLogs { get; set; } + } +}