Skip to content

Commit 360226a

Browse files
feat: Support credential type parameter for NLog.
1 parent 2d5406f commit 360226a

File tree

10 files changed

+160
-169
lines changed

10 files changed

+160
-169
lines changed

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog.IntegrationTests/CredentialTest.cs

Lines changed: 0 additions & 107 deletions
This file was deleted.

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog.IntegrationTests/Google.Cloud.Logging.NLog.IntegrationTests.csproj

Lines changed: 0 additions & 21 deletions
This file was deleted.

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog.IntegrationTests/NLogFixture.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright 2025 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Newtonsoft.Json;
16+
using NLog;
17+
using System;
18+
using System.IO;
19+
using Xunit;
20+
21+
namespace Google.Cloud.Logging.NLog.Tests;
22+
23+
public class CredentialTests
24+
{
25+
private const string FakeServiceAccountCredential = @"{
26+
""private_key_id"": ""PRIVATE_KEY_ID"",
27+
""private_key"": ""-----BEGIN PRIVATE KEY-----
28+
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJJM6HT4s6btOsfe
29+
2x4zrzrwSUtmtR37XTTi0sPARTDF8uzmXy8UnE5RcVJzEH5T2Ssz/ylX4Sl/CI4L
30+
no1l8j9GiHJb49LSRjWe4Yx936q0Xj9H0R1HTxvjUPqwAsTwy2fKBTog+q1frqc9
31+
o8s2r6LYivUGDVbhuUzCaMJsf+x3AgMBAAECgYEAi0FTXsu/zRswAUGaViQiHjrL
32+
uU65BSHXNVjV/2fLNEKnGWGqpli68z1IXY+S2nwbUak7rnGsq9/0F6jtsW+hZbLk
33+
KXUOuuExpeC5Kd6ngWX/f2jqmhlUabiQijU9cVk7pMq8EHkRtvlosnMTUAEzempu
34+
QUPwn1PZHhmJkBvZ4lECQQDCErrxl+e3BwUDcS0yVEEmCNSG6xdXs2878b8rzbe7
35+
3Mmi6SuuOLi3PU92J+j+f/MOdtYrk13mEDdYmd5dhrt5AkEAwPvDEsDT/W4y4h5n
36+
gv1awGBA5aLFE1JNWM/Gwn4D1cGpEDHKFREaBtxMDCASpHJuw8r7zUywpKhmBZcf
37+
GS37bwJANdSAKfbafLfjuhqwUJ9yGpykZm/a36aTmerp/bpn1iHdg+RtCzwMcDb/
38+
TWSwibbvsflgWmHbz657y4WSWhq+8QJAWrpCNN/ZCk2zuGDo80lfUBAwkoVat8G6
39+
wWU1oZyS+vzIGef+hLb8kHsjeZPej9eIwZ39kcBbT54oELrCkRjwGwJAQ8V2A7lT
40+
ZUp8AsbVqF6rbLiiUfJMo2btGclQu4DEVyS+ymFA65tXDLUuR9EDqJYdqHNZJ5B8
41+
4Z5p2prkjWTLcA\u003d\u003d
42+
-----END PRIVATE KEY-----"",
43+
""client_email"": ""CLIENT_EMAIL"",
44+
""client_id"": ""CLIENT_ID"",
45+
""project_id"": ""PROJECT_ID"",
46+
""type"": ""service_account""}";
47+
48+
private const string FakeUserCredential = @"{
49+
""client_id"": ""CLIENT_ID"",
50+
""client_secret"": ""CLIENT_SECRET"",
51+
""refresh_token"": ""REFRESH_TOKEN"",
52+
""type"": ""authorized_user""}";
53+
54+
[Fact]
55+
public void LoadCredentialFromFile_Succeeds()
56+
{
57+
using var tempCredFile = new TempFile(FakeServiceAccountCredential);
58+
InitializeNLog(GetXml($"credentialFile='{tempCredFile.Path}'"));
59+
}
60+
61+
[Fact]
62+
public void LoadCredentialFromJson_Succeeds() => InitializeNLog(GetXml($"credentialJson='{FakeServiceAccountCredential}'"));
63+
64+
[Fact]
65+
public void LoadCredentialFromFile_BadFormat_Fails()
66+
{
67+
using var tempCredFile = new TempFile("bad json");
68+
var exception = Record.Exception(() => InitializeNLog(GetXml($"credentialFile='{tempCredFile.Path}'")));
69+
Assert.IsType<InvalidOperationException>(exception);
70+
Assert.IsType<JsonReaderException>(exception.InnerException);
71+
}
72+
73+
[Fact]
74+
public void LoadCredentialFromJson_BadFormat_Fails()
75+
{
76+
var exception = Record.Exception(() => InitializeNLog(GetXml("credentialJson='terrible json'")));
77+
Assert.IsType<InvalidOperationException>(exception);
78+
Assert.IsType<JsonReaderException>(exception.InnerException);
79+
}
80+
81+
[Fact]
82+
public void LoadCredentialFromFile_FileDoesNotExist_Fails() =>
83+
Assert.Throws<FileNotFoundException>(() => InitializeNLog(GetXml("credentialFile='non-existent-file'")));
84+
85+
86+
[Fact]
87+
public void LoadCredential_MultipleCredentials_Fails()
88+
{
89+
using var tempCredFile = new TempFile(FakeServiceAccountCredential);
90+
var exception = Assert.Throws<InvalidOperationException>(() =>
91+
InitializeNLog(GetXml($"credentialFile='{tempCredFile.Path}' credentialJson='{FakeServiceAccountCredential}'")));
92+
Assert.IsType<InvalidOperationException>(exception);
93+
Assert.Contains("CredentialFile", exception.Message);
94+
Assert.Contains("CredentialJson", exception.Message);
95+
}
96+
97+
[Fact]
98+
public void LoadUserCredentialFromJson_WithCredentialType_Succeeds() =>
99+
InitializeNLog(GetXml($"credentialJson='{FakeUserCredential}' credentialType='authorized_user'"));
100+
101+
[Fact]
102+
public void LoadUserCredentialFromJson_WithWrongCredentialType_Fails() =>
103+
Assert.Throws<InvalidOperationException>(() => InitializeNLog(GetXml($"credentialJson='{FakeServiceAccountCredential}' credentialType='authorized_user'")));
104+
105+
private static string GetXml(string attributes) =>
106+
"<nlog xmlns='http://www.nlog-project.org/schemas/NLog.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>" +
107+
"<extensions><add assembly='Google.Cloud.Logging.NLog'/></extensions>" +
108+
"<targets>" +
109+
$"<target name='stackdriver' xsi:type='GoogleStackdriver' projectId='test-project-id' {attributes} />" +
110+
"</targets>" +
111+
"</nlog>";
112+
113+
private static void InitializeNLog(string config)
114+
{
115+
using var tempConfigFile = new TempFile(config);
116+
using var logFactory = new LogFactory();
117+
logFactory.ThrowExceptions = true;
118+
logFactory.Setup().LoadConfigurationFromFile(tempConfigFile.Path);
119+
Assert.NotNull(logFactory.Configuration);
120+
}
121+
122+
private sealed class TempFile : IDisposable
123+
{
124+
public string Path { get; }
125+
126+
public TempFile(string content = null)
127+
{
128+
Path = System.IO.Path.GetTempFileName();
129+
if (content != null)
130+
{
131+
File.WriteAllText(Path, content);
132+
}
133+
}
134+
135+
public void Dispose()
136+
{
137+
if(File.Exists(Path))
138+
{
139+
File.Delete(Path);
140+
}
141+
}
142+
}
143+
}

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog.sln

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ VisualStudioVersion = 17.2.32516.85
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Logging.NLog", "Google.Cloud.Logging.NLog\Google.Cloud.Logging.NLog.csproj", "{6D49EF6F-7560-6F97-397E-078969D8757B}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Logging.NLog.IntegrationTests", "Google.Cloud.Logging.NLog.IntegrationTests\Google.Cloud.Logging.NLog.IntegrationTests.csproj", "{ACEC53F3-0EFD-91A5-CF74-305332D20A9F}"
9-
EndProject
108
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Logging.NLog.Snippets", "Google.Cloud.Logging.NLog.Snippets\Google.Cloud.Logging.NLog.Snippets.csproj", "{881DAAA0-40C6-1230-750F-3A3190D4F3A7}"
119
EndProject
1210
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Logging.NLog.Tests", "Google.Cloud.Logging.NLog.Tests\Google.Cloud.Logging.NLog.Tests.csproj", "{940DDEF4-FECB-9507-8D65-591A7A7D8BD6}"
@@ -23,10 +21,6 @@ Global
2321
{6D49EF6F-7560-6F97-397E-078969D8757B}.Debug|Any CPU.Build.0 = Debug|Any CPU
2422
{6D49EF6F-7560-6F97-397E-078969D8757B}.Release|Any CPU.ActiveCfg = Release|Any CPU
2523
{6D49EF6F-7560-6F97-397E-078969D8757B}.Release|Any CPU.Build.0 = Release|Any CPU
26-
{ACEC53F3-0EFD-91A5-CF74-305332D20A9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27-
{ACEC53F3-0EFD-91A5-CF74-305332D20A9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28-
{ACEC53F3-0EFD-91A5-CF74-305332D20A9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
29-
{ACEC53F3-0EFD-91A5-CF74-305332D20A9F}.Release|Any CPU.Build.0 = Release|Any CPU
3024
{881DAAA0-40C6-1230-750F-3A3190D4F3A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
3125
{881DAAA0-40C6-1230-750F-3A3190D4F3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
3226
{881DAAA0-40C6-1230-750F-3A3190D4F3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
</PropertyGroup>
1010
<ItemGroup>
1111
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="All" />
12-
<PackageReference Include="Google.Api.Gax.Grpc" />
12+
<PackageReference Include="Google.Api.Gax" VersionOverride="[4.13.0, 5.0.0)" />
13+
<PackageReference Include="Google.Api.Gax.Grpc" VersionOverride="[4.13.0, 5.0.0)" />
1314
<PackageReference Include="Google.Cloud.DevTools.Common" VersionOverride="[3.3.0, 4.0.0)" />
1415
<PackageReference Include="Google.Cloud.Logging.V2" VersionOverride="[4.5.0, 5.0.0)" />
1516
<PackageReference Include="Grpc.Core" PrivateAssets="None" Condition="'$(TargetFramework)'=='net462'" />

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog/GoogleStackdriverTarget.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,12 @@ private GoogleCredential GetCredentialFromConfiguration()
276276
var nullLogEvent = LogEventInfo.CreateNullEvent();
277277
var credentialFile = CredentialFile?.Render(nullLogEvent);
278278
var credentialJson = CredentialJson?.Render(nullLogEvent);
279+
var credentialType = CredentialType?.Render(nullLogEvent);
279280
GaxPreconditions.CheckState(string.IsNullOrWhiteSpace(credentialFile) || string.IsNullOrWhiteSpace(credentialJson),
280281
$"{nameof(CredentialFile)} and {nameof(CredentialJson)} must not both be set.");
281282
var credential =
282-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
283-
!string.IsNullOrWhiteSpace(credentialFile) ? GoogleCredential.FromFile(credentialFile) :
284-
!string.IsNullOrWhiteSpace(credentialJson) ? GoogleCredential.FromJson(credentialJson) :
285-
#pragma warning restore CS0618
283+
!string.IsNullOrWhiteSpace(credentialFile) ? CredentialFactory.FromFile(credentialFile, credentialType) :
284+
!string.IsNullOrWhiteSpace(credentialJson) ? CredentialFactory.FromJson(credentialJson, credentialType) :
286285
null;
287286
if (credential == null)
288287
{

apis/Google.Cloud.Logging.NLog/Google.Cloud.Logging.NLog/GoogleStackdriverTarget_Configuration.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 Google LLC
1+
// Copyright 2018 Google LLC
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using Google.Apis.Auth.OAuth2;
1516
using Google.Cloud.Logging.V2;
1617
using Google.Protobuf.WellKnownTypes;
1718
using NLog.Config;
@@ -40,6 +41,13 @@ public partial class GoogleStackdriverTarget
4041
/// </summary>
4142
public Layout CredentialJson { get; set; }
4243

44+
/// <summary>
45+
/// The type of credential to load when either <see cref="CredentialFile"/> or <see cref="CredentialJson"/> is specified.
46+
/// Valid strings can be found in the <see cref="JsonCredentialParameters"/> class.
47+
/// Defaults to <see cref="JsonCredentialParameters.ServiceAccountCredentialType"/>. Must not be null.
48+
/// </summary>
49+
public Layout CredentialType { get; set; } = JsonCredentialParameters.ServiceAccountCredentialType;
50+
4351
/// <summary>
4452
/// If set, disables resource-type detection based on platform,
4553
/// so ResourceType will default to "global" if not manually set.

apis/Google.Cloud.Logging.NLog/docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ then the `projectId="PROJECT_ID"` configuration setting can be omitted; it will
4646
- `ContextProperty` - Allows custom labels to be added to the log entries as extra metadata.
4747
- `CredentialFile` - Credentials using a JSON file, instead of using the application default credentials
4848
- `CredentialJson` - Credentials using raw JSON input, instead of using the application default credentials
49+
- `CredentialType` - Specifies the type of credential being used (Default = "service_account"). The value must be a valid type as defined on [JsonCredentialParameters](https://docs.cloud.google.com/dotnet/docs/reference/Google.Apis/latest/Google.Apis.Auth.OAuth2.JsonCredentialParameters); possible values include "service_account", "authorized_user", "external_account", and "impersonated_service_account".
4950
- `IncludeCallSite` - Include `LogEntrySourceLocation` extracted from NLog callsite. (This only includes the function name.)
5051
- `IncludeCallSiteStackTrace` - Include `LogEntrySourceLocation` extracted from NLog callsite. (This also includes file name and line number.)
5152
- `IncludeEventProperties` - Includes structured logging properties from NLog `LogEventInfo.Properties`.

0 commit comments

Comments
 (0)