You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

392 lines
11 KiB

  1. // Copyright 2016 Google LLC
  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. // Tests that require access to unexported names of the logging package.
  15. package logging
  16. import (
  17. "encoding/json"
  18. "net/http"
  19. "net/url"
  20. "testing"
  21. "time"
  22. "cloud.google.com/go/internal/testutil"
  23. "github.com/golang/protobuf/proto"
  24. durpb "github.com/golang/protobuf/ptypes/duration"
  25. structpb "github.com/golang/protobuf/ptypes/struct"
  26. "google.golang.org/api/logging/v2"
  27. "google.golang.org/api/support/bundler"
  28. mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
  29. logtypepb "google.golang.org/genproto/googleapis/logging/type"
  30. )
  31. func TestLoggerCreation(t *testing.T) {
  32. const logID = "testing"
  33. c := &Client{parent: "projects/PROJECT_ID"}
  34. customResource := &mrpb.MonitoredResource{
  35. Type: "global",
  36. Labels: map[string]string{
  37. "project_id": "ANOTHER_PROJECT",
  38. },
  39. }
  40. defaultBundler := &bundler.Bundler{
  41. DelayThreshold: DefaultDelayThreshold,
  42. BundleCountThreshold: DefaultEntryCountThreshold,
  43. BundleByteThreshold: DefaultEntryByteThreshold,
  44. BundleByteLimit: 0,
  45. BufferedByteLimit: DefaultBufferedByteLimit,
  46. }
  47. for _, test := range []struct {
  48. options []LoggerOption
  49. wantLogger *Logger
  50. defaultResource bool
  51. wantBundler *bundler.Bundler
  52. }{
  53. {
  54. options: nil,
  55. wantLogger: &Logger{},
  56. defaultResource: true,
  57. wantBundler: defaultBundler,
  58. },
  59. {
  60. options: []LoggerOption{
  61. CommonResource(nil),
  62. CommonLabels(map[string]string{"a": "1"}),
  63. },
  64. wantLogger: &Logger{
  65. commonResource: nil,
  66. commonLabels: map[string]string{"a": "1"},
  67. },
  68. wantBundler: defaultBundler,
  69. },
  70. {
  71. options: []LoggerOption{CommonResource(customResource)},
  72. wantLogger: &Logger{commonResource: customResource},
  73. wantBundler: defaultBundler,
  74. },
  75. {
  76. options: []LoggerOption{
  77. DelayThreshold(time.Minute),
  78. EntryCountThreshold(99),
  79. EntryByteThreshold(17),
  80. EntryByteLimit(18),
  81. BufferedByteLimit(19),
  82. },
  83. wantLogger: &Logger{},
  84. defaultResource: true,
  85. wantBundler: &bundler.Bundler{
  86. DelayThreshold: time.Minute,
  87. BundleCountThreshold: 99,
  88. BundleByteThreshold: 17,
  89. BundleByteLimit: 18,
  90. BufferedByteLimit: 19,
  91. },
  92. },
  93. } {
  94. gotLogger := c.Logger(logID, test.options...)
  95. if got, want := gotLogger.commonResource, test.wantLogger.commonResource; !test.defaultResource && !proto.Equal(got, want) {
  96. t.Errorf("%v: resource: got %v, want %v", test.options, got, want)
  97. }
  98. if got, want := gotLogger.commonLabels, test.wantLogger.commonLabels; !testutil.Equal(got, want) {
  99. t.Errorf("%v: commonLabels: got %v, want %v", test.options, got, want)
  100. }
  101. if got, want := gotLogger.bundler.DelayThreshold, test.wantBundler.DelayThreshold; got != want {
  102. t.Errorf("%v: DelayThreshold: got %v, want %v", test.options, got, want)
  103. }
  104. if got, want := gotLogger.bundler.BundleCountThreshold, test.wantBundler.BundleCountThreshold; got != want {
  105. t.Errorf("%v: BundleCountThreshold: got %v, want %v", test.options, got, want)
  106. }
  107. if got, want := gotLogger.bundler.BundleByteThreshold, test.wantBundler.BundleByteThreshold; got != want {
  108. t.Errorf("%v: BundleByteThreshold: got %v, want %v", test.options, got, want)
  109. }
  110. if got, want := gotLogger.bundler.BundleByteLimit, test.wantBundler.BundleByteLimit; got != want {
  111. t.Errorf("%v: BundleByteLimit: got %v, want %v", test.options, got, want)
  112. }
  113. if got, want := gotLogger.bundler.BufferedByteLimit, test.wantBundler.BufferedByteLimit; got != want {
  114. t.Errorf("%v: BufferedByteLimit: got %v, want %v", test.options, got, want)
  115. }
  116. }
  117. }
  118. func TestToProtoStruct(t *testing.T) {
  119. v := struct {
  120. Foo string `json:"foo"`
  121. Bar int `json:"bar,omitempty"`
  122. Baz []float64 `json:"baz"`
  123. Moo map[string]interface{} `json:"moo"`
  124. }{
  125. Foo: "foovalue",
  126. Baz: []float64{1.1},
  127. Moo: map[string]interface{}{
  128. "a": 1,
  129. "b": "two",
  130. "c": true,
  131. },
  132. }
  133. got, err := toProtoStruct(v)
  134. if err != nil {
  135. t.Fatal(err)
  136. }
  137. want := &structpb.Struct{
  138. Fields: map[string]*structpb.Value{
  139. "foo": {Kind: &structpb.Value_StringValue{StringValue: v.Foo}},
  140. "baz": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{
  141. {Kind: &structpb.Value_NumberValue{NumberValue: 1.1}},
  142. }}}},
  143. "moo": {Kind: &structpb.Value_StructValue{
  144. StructValue: &structpb.Struct{
  145. Fields: map[string]*structpb.Value{
  146. "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
  147. "b": {Kind: &structpb.Value_StringValue{StringValue: "two"}},
  148. "c": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  149. },
  150. },
  151. }},
  152. },
  153. }
  154. if !proto.Equal(got, want) {
  155. t.Errorf("got %+v\nwant %+v", got, want)
  156. }
  157. // Non-structs should fail to convert.
  158. for v := range []interface{}{3, "foo", []int{1, 2, 3}} {
  159. _, err := toProtoStruct(v)
  160. if err == nil {
  161. t.Errorf("%v: got nil, want error", v)
  162. }
  163. }
  164. // Test fast path.
  165. got, err = toProtoStruct(want)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. if got != want {
  170. t.Error("got and want should be identical, but are not")
  171. }
  172. }
  173. func TestToLogEntryPayload(t *testing.T) {
  174. var logger Logger
  175. for _, test := range []struct {
  176. in interface{}
  177. wantText string
  178. wantStruct *structpb.Struct
  179. }{
  180. {
  181. in: "string",
  182. wantText: "string",
  183. },
  184. {
  185. in: map[string]interface{}{"a": 1, "b": true},
  186. wantStruct: &structpb.Struct{
  187. Fields: map[string]*structpb.Value{
  188. "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
  189. "b": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  190. },
  191. },
  192. },
  193. {
  194. in: json.RawMessage([]byte(`{"a": 1, "b": true}`)),
  195. wantStruct: &structpb.Struct{
  196. Fields: map[string]*structpb.Value{
  197. "a": {Kind: &structpb.Value_NumberValue{NumberValue: 1}},
  198. "b": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  199. },
  200. },
  201. },
  202. } {
  203. e, err := logger.toLogEntry(Entry{Payload: test.in})
  204. if err != nil {
  205. t.Fatalf("%+v: %v", test.in, err)
  206. }
  207. if test.wantStruct != nil {
  208. got := e.GetJsonPayload()
  209. if !proto.Equal(got, test.wantStruct) {
  210. t.Errorf("%+v: got %s, want %s", test.in, got, test.wantStruct)
  211. }
  212. } else {
  213. got := e.GetTextPayload()
  214. if got != test.wantText {
  215. t.Errorf("%+v: got %s, want %s", test.in, got, test.wantText)
  216. }
  217. }
  218. }
  219. }
  220. func TestToLogEntryTrace(t *testing.T) {
  221. logger := &Logger{client: &Client{parent: "projects/P"}}
  222. // Verify that we get the trace from the HTTP request if it isn't
  223. // provided by the caller.
  224. u := &url.URL{Scheme: "http"}
  225. for _, test := range []struct {
  226. in Entry
  227. want logging.LogEntry
  228. }{
  229. {Entry{}, logging.LogEntry{}},
  230. {Entry{Trace: "t1"}, logging.LogEntry{Trace: "t1"}},
  231. {
  232. Entry{
  233. HTTPRequest: &HTTPRequest{
  234. Request: &http.Request{URL: u, Header: http.Header{"foo": {"bar"}}},
  235. },
  236. },
  237. logging.LogEntry{},
  238. },
  239. {
  240. Entry{
  241. HTTPRequest: &HTTPRequest{
  242. Request: &http.Request{
  243. URL: u,
  244. Header: http.Header{"X-Cloud-Trace-Context": {"t2"}},
  245. },
  246. },
  247. },
  248. logging.LogEntry{Trace: "projects/P/traces/t2"},
  249. },
  250. {
  251. Entry{
  252. HTTPRequest: &HTTPRequest{
  253. Request: &http.Request{
  254. URL: u,
  255. Header: http.Header{"X-Cloud-Trace-Context": {"t3"}},
  256. },
  257. },
  258. Trace: "t4",
  259. },
  260. logging.LogEntry{Trace: "t4"},
  261. },
  262. {Entry{Trace: "t1", SpanID: "007"}, logging.LogEntry{Trace: "t1", SpanId: "007"}},
  263. } {
  264. e, err := logger.toLogEntry(test.in)
  265. if err != nil {
  266. t.Fatalf("%+v: %v", test.in, err)
  267. }
  268. if got := e.Trace; got != test.want.Trace {
  269. t.Errorf("%+v: got %q, want %q", test.in, got, test.want.Trace)
  270. }
  271. if got := e.SpanId; got != test.want.SpanId {
  272. t.Errorf("%+v: got %q, want %q", test.in, got, test.want.SpanId)
  273. }
  274. }
  275. }
  276. func TestFromHTTPRequest(t *testing.T) {
  277. const testURL = "http:://example.com/path?q=1"
  278. u, err := url.Parse(testURL)
  279. if err != nil {
  280. t.Fatal(err)
  281. }
  282. req := &HTTPRequest{
  283. Request: &http.Request{
  284. Method: "GET",
  285. URL: u,
  286. Header: map[string][]string{
  287. "User-Agent": {"user-agent"},
  288. "Referer": {"referer"},
  289. },
  290. },
  291. RequestSize: 100,
  292. Status: 200,
  293. ResponseSize: 25,
  294. Latency: 100 * time.Second,
  295. LocalIP: "127.0.0.1",
  296. RemoteIP: "10.0.1.1",
  297. CacheHit: true,
  298. CacheValidatedWithOriginServer: true,
  299. }
  300. got := fromHTTPRequest(req)
  301. want := &logtypepb.HttpRequest{
  302. RequestMethod: "GET",
  303. RequestUrl: testURL,
  304. RequestSize: 100,
  305. Status: 200,
  306. ResponseSize: 25,
  307. Latency: &durpb.Duration{Seconds: 100},
  308. UserAgent: "user-agent",
  309. ServerIp: "127.0.0.1",
  310. RemoteIp: "10.0.1.1",
  311. Referer: "referer",
  312. CacheHit: true,
  313. CacheValidatedWithOriginServer: true,
  314. }
  315. if !proto.Equal(got, want) {
  316. t.Errorf("got %+v\nwant %+v", got, want)
  317. }
  318. }
  319. func TestMonitoredResource(t *testing.T) {
  320. for _, test := range []struct {
  321. parent string
  322. want *mrpb.MonitoredResource
  323. }{
  324. {
  325. "projects/P",
  326. &mrpb.MonitoredResource{
  327. Type: "project",
  328. Labels: map[string]string{"project_id": "P"},
  329. },
  330. },
  331. {
  332. "folders/F",
  333. &mrpb.MonitoredResource{
  334. Type: "folder",
  335. Labels: map[string]string{"folder_id": "F"},
  336. },
  337. },
  338. {
  339. "billingAccounts/B",
  340. &mrpb.MonitoredResource{
  341. Type: "billing_account",
  342. Labels: map[string]string{"account_id": "B"},
  343. },
  344. },
  345. {
  346. "organizations/123",
  347. &mrpb.MonitoredResource{
  348. Type: "organization",
  349. Labels: map[string]string{"organization_id": "123"},
  350. },
  351. },
  352. {
  353. "unknown/X",
  354. &mrpb.MonitoredResource{
  355. Type: "global",
  356. Labels: map[string]string{"project_id": "X"},
  357. },
  358. },
  359. {
  360. "whatever",
  361. &mrpb.MonitoredResource{
  362. Type: "global",
  363. Labels: map[string]string{"project_id": "whatever"},
  364. },
  365. },
  366. } {
  367. got := monitoredResource(test.parent)
  368. if !testutil.Equal(got, test.want) {
  369. t.Errorf("%q: got %+v, want %+v", test.parent, got, test.want)
  370. }
  371. }
  372. }
  373. // Used by the tests in logging_test.
  374. func SetNow(f func() time.Time) {
  375. now = f
  376. }