diff --git a/plugins/node/opentelemetry-instrumentation-winston/.eslintignore b/plugins/node/opentelemetry-instrumentation-winston/.eslintignore
new file mode 100644
index 0000000000..378eac25d3
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/.eslintignore
@@ -0,0 +1 @@
+build
diff --git a/plugins/node/opentelemetry-instrumentation-winston/.eslintrc.js b/plugins/node/opentelemetry-instrumentation-winston/.eslintrc.js
new file mode 100644
index 0000000000..6aeb0710ef
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/.eslintrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+    "env": {
+        "mocha": true,
+        "node": true
+    },
+    ...require('../../../eslint.config.js'),
+}
diff --git a/plugins/node/opentelemetry-instrumentation-winston/.npmignore b/plugins/node/opentelemetry-instrumentation-winston/.npmignore
new file mode 100644
index 0000000000..9505ba9450
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/.npmignore
@@ -0,0 +1,4 @@
+/bin
+/coverage
+/doc
+/test
diff --git a/plugins/node/opentelemetry-instrumentation-winston/LICENSE b/plugins/node/opentelemetry-instrumentation-winston/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/plugins/node/opentelemetry-instrumentation-winston/README.md b/plugins/node/opentelemetry-instrumentation-winston/README.md
new file mode 100644
index 0000000000..32351d3362
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/README.md
@@ -0,0 +1,79 @@
+# OpenTelemetry instrumentation for winston
+
+[![NPM Published Version][npm-img]][npm-url]
+[![dependencies][dependencies-image]][dependencies-url]
+[![devDependencies][devDependencies-image]][devDependencies-url]
+[![Apache License][license-image]][license-image]
+
+This module provides automatic instrumentation for injection of trace context to [`winston`](https://www.npmjs.com/package/winston).
+
+## Installation
+
+```bash
+npm install --save @opentelemetry/instrumentation-winston
+```
+
+## Usage
+
+```js
+const { NodeTracerProvider } = require('@opentelemetry/node');
+const { WinstonInstrumentation } = require('@opentelemetry/instrumentation-winston');
+const { registerInstrumentations } = require('@opentelemetry/instrumentation');
+
+const provider = new NodeTracerProvider();
+provider.register();
+
+registerInstrumentations({
+  instrumentations: [
+    new WinstonInstrumentation({
+      // Optional hook to insert additional context to log metadata.
+      // Called after trace context is injected to metadata.
+      logHook: (record, span) => {
+        record['resource.service.name'] = provider.resource.attributes['service.name'];
+      },
+    }),
+    // other instrumentations
+  ],
+  tracerProvider: provider,
+});
+
+const winston = require('winston');
+const logger = winston.createLogger({
+  transports: [new winston.transports.Console()],
+})
+logger.info('foobar');
+// {"message":"foobar","trace_id":"e21c7a95fff34e04f77c7bd518779621","span_id":"b7589a981fde09f4","trace_flags":"01", ...}
+```
+
+### Fields added to Winston metadata
+
+For the current active span, the following fields are injected:
+* `trace_id`
+* `span_id`
+* `trace_flags`
+
+When no span context is active or the span context is invalid, injection is skipped.
+
+### Supported versions
+
+`1.x`, `2.x`, `3.x`
+
+## Useful links
+
+- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
+- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
+- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
+
+## License
+
+Apache 2.0 - See [LICENSE][license-url] for more information.
+
+[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions
+[license-url]: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/LICENSE
+[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat
+[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js-contrib.svg?path=plugins%2Fnode%2Fopentelemetry-instrumentation-winston
+[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=plugins%2Fnode%2Fopentelemetry-instrumentation-winston
+[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js-contrib.svg?path=plugins%2Fnode%2Fopentelemetry-instrumentation-winston&type=dev
+[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=plugins%2Fnode%2Fopentelemetry-instrumentation-winston&type=dev
+[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-winston
+[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-winston.svg
diff --git a/plugins/node/opentelemetry-instrumentation-winston/package.json b/plugins/node/opentelemetry-instrumentation-winston/package.json
new file mode 100644
index 0000000000..f7a71ba39b
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/package.json
@@ -0,0 +1,69 @@
+{
+  "name": "@opentelemetry/instrumentation-winston",
+  "version": "0.15.0",
+  "description": "OpenTelemetry instrumentation for winston",
+  "main": "build/src/index.js",
+  "types": "build/src/index.d.ts",
+  "repository": "open-telemetry/opentelemetry-js-contrib",
+  "scripts": {
+    "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'",
+    "tdd": "npm run test -- --watch-extensions ts --watch",
+    "clean": "rimraf build/*",
+    "lint": "eslint . --ext .ts",
+    "lint:fix": "eslint . --ext .ts --fix",
+    "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
+    "precompile": "tsc --version",
+    "prepare": "npm run compile",
+    "version:update": "node ../../../scripts/version-update.js",
+    "compile": "npm run version:update && tsc -p ."
+  },
+  "keywords": [
+    "opentelemetry",
+    "logging",
+    "nodejs",
+    "tracing",
+    "profiling",
+    "instrumentation",
+    "winston"
+  ],
+  "author": "OpenTelemetry Authors",
+  "license": "Apache-2.0",
+  "engines": {
+    "node": ">=8.5.0"
+  },
+  "files": [
+    "build/src/**/*.js",
+    "build/src/**/*.d.ts",
+    "doc",
+    "LICENSE",
+    "README.md"
+  ],
+  "publishConfig": {
+    "access": "public"
+  },
+  "devDependencies": {
+    "@opentelemetry/context-async-hooks": "0.18.2",
+    "@opentelemetry/node": "0.18.2",
+    "@opentelemetry/tracing": "0.18.2",
+    "@types/mocha": "7.0.2",
+    "@types/node": "14.0.27",
+    "@types/sinon": "9.0.4",
+    "codecov": "3.7.2",
+    "gts": "3.1.0",
+    "mocha": "7.2.0",
+    "nyc": "15.1.0",
+    "rimraf": "3.0.2",
+    "sinon": "9.0.2",
+    "ts-mocha": "8.0.0",
+    "ts-node": "9.0.0",
+    "tslint-consistent-codestyle": "1.16.0",
+    "tslint-microsoft-contrib": "6.2.0",
+    "typescript": "4.1.3",
+    "winston": "^3.3.3",
+    "winston2": "npm:winston@^2.4.4"
+  },
+  "dependencies": {
+    "@opentelemetry/api": "^0.18.0",
+    "@opentelemetry/instrumentation": "^0.18.0"
+  }
+}
diff --git a/plugins/node/opentelemetry-instrumentation-winston/src/index.ts b/plugins/node/opentelemetry-instrumentation-winston/src/index.ts
new file mode 100644
index 0000000000..3c20533066
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/src/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.
+ */
+
+export * from './winston';
diff --git a/plugins/node/opentelemetry-instrumentation-winston/src/types.ts b/plugins/node/opentelemetry-instrumentation-winston/src/types.ts
new file mode 100644
index 0000000000..60bd86c8f4
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/src/types.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.
+ */
+
+import { Span } from '@opentelemetry/api';
+import { InstrumentationConfig } from '@opentelemetry/instrumentation';
+import type { Logger as Winston3Logger } from 'winston';
+import type {
+  LoggerInstance as Winston2Logger,
+  LogMethod as Winston2LogMethod,
+} from 'winston2';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type LogHookFunction = (span: Span, record: Record<string, any>) => void;
+
+export interface WinstonInstrumentationConfig extends InstrumentationConfig {
+  logHook?: LogHookFunction;
+}
+
+export type Winston3LogMethod = Winston3Logger['write'];
+export type { Winston3Logger };
+
+export type { Winston2LogMethod };
+export type Winston2LoggerModule = {
+  Logger: Winston2Logger & { prototype: { log: Winston2LogMethod } };
+};
diff --git a/plugins/node/opentelemetry-instrumentation-winston/src/version.ts b/plugins/node/opentelemetry-instrumentation-winston/src/version.ts
new file mode 100644
index 0000000000..1e4172026f
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/src/version.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.
+ */
+
+// this is autogenerated file, see scripts/version-update.js
+export const VERSION = '0.15.0';
diff --git a/plugins/node/opentelemetry-instrumentation-winston/src/winston.ts b/plugins/node/opentelemetry-instrumentation-winston/src/winston.ts
new file mode 100644
index 0000000000..b18e416337
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/src/winston.ts
@@ -0,0 +1,218 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.
+ */
+
+import {
+  context,
+  diag,
+  getSpan,
+  isSpanContextValid,
+  Span,
+  SpanContext,
+} from '@opentelemetry/api';
+import {
+  InstrumentationBase,
+  InstrumentationNodeModuleDefinition,
+  InstrumentationNodeModuleFile,
+  isWrapped,
+  safeExecuteInTheMiddle,
+} from '@opentelemetry/instrumentation';
+import {
+  WinstonInstrumentationConfig,
+  Winston2LogMethod,
+  Winston2LoggerModule,
+  Winston3LogMethod,
+  Winston3Logger,
+} from './types';
+import { VERSION } from './version';
+
+const winston3Versions = ['>=3 <4'];
+const winstonPre3Versions = ['>=1 <3'];
+
+export class WinstonInstrumentation extends InstrumentationBase {
+  constructor(config: WinstonInstrumentationConfig = {}) {
+    super('@opentelemetry/instrumentation-winston', VERSION, config);
+  }
+
+  protected init() {
+    return [
+      new InstrumentationNodeModuleDefinition<{}>(
+        'winston',
+        winston3Versions,
+        moduleExports => moduleExports,
+        () => {},
+        [
+          new InstrumentationNodeModuleFile<Winston3Logger>(
+            'winston/lib/winston/logger.js',
+            winston3Versions,
+            logger => {
+              if (isWrapped(logger.prototype['write'])) {
+                this._unwrap(logger.prototype, 'write');
+              }
+
+              this._wrap(logger.prototype, 'write', this._getPatchedWrite());
+              return logger;
+            },
+            logger => {
+              if (logger === undefined) return;
+              this._unwrap(logger.prototype, 'write');
+            }
+          ),
+        ]
+      ),
+      new InstrumentationNodeModuleDefinition<{}>(
+        'winston',
+        winstonPre3Versions,
+        moduleExports => moduleExports,
+        () => {},
+        [
+          new InstrumentationNodeModuleFile<Winston2LoggerModule>(
+            'winston/lib/winston/logger.js',
+            winstonPre3Versions,
+            fileExports => {
+              const proto = fileExports.Logger.prototype;
+
+              if (isWrapped(proto.log)) {
+                this._unwrap(proto, 'log');
+              }
+
+              this._wrap(proto, 'log', this._getPatchedLog());
+
+              return fileExports;
+            },
+            fileExports => {
+              if (fileExports === undefined) return;
+              this._unwrap(fileExports.Logger.prototype, 'log');
+            }
+          ),
+        ]
+      ),
+    ];
+  }
+
+  getConfig(): WinstonInstrumentationConfig {
+    return this._config;
+  }
+
+  setConfig(config: WinstonInstrumentationConfig) {
+    this._config = config;
+  }
+
+  private _callHook(span: Span, record: Record<string, string>) {
+    const hook = this.getConfig().logHook;
+
+    if (!hook) {
+      return;
+    }
+
+    safeExecuteInTheMiddle(
+      () => hook(span, record),
+      err => {
+        if (err) {
+          diag.error('winston instrumentation: error calling logHook', err);
+        }
+      },
+      true
+    );
+  }
+
+  private _getPatchedWrite() {
+    return (original: Winston3LogMethod) => {
+      const instrumentation = this;
+      return function patchedWrite(
+        this: never,
+        ...args: Parameters<typeof original>
+      ) {
+        const span = getSpan(context.active());
+
+        if (!span) {
+          return original.apply(this, args);
+        }
+
+        const spanContext = span.context();
+
+        if (!isSpanContextValid(spanContext)) {
+          return original.apply(this, args);
+        }
+
+        const record = args[0];
+        injectRecord(spanContext, record);
+        instrumentation._callHook(span, record);
+
+        return original.apply(this, args);
+      };
+    };
+  }
+
+  private _getPatchedLog() {
+    return (original: Winston2LogMethod) => {
+      const instrumentation = this;
+      return function patchedLog(
+        this: unknown,
+        ...args: Parameters<typeof original>
+      ) {
+        const span = getSpan(context.active());
+
+        if (!span) {
+          return original.apply(this, args);
+        }
+
+        const spanContext = span.context();
+
+        if (!isSpanContextValid(spanContext)) {
+          return original.apply(this, args);
+        }
+
+        for (let i = args.length - 1; i >= 0; i--) {
+          if (typeof args[i] === 'object') {
+            const record = args[i];
+            injectRecord(spanContext, record);
+            instrumentation._callHook(span, record);
+            return original.apply(this, args);
+          }
+        }
+
+        const record = injectRecord(spanContext);
+
+        const insertAt =
+          typeof args[args.length - 1] === 'function'
+            ? args.length - 1
+            : args.length;
+
+        args.splice(insertAt, 0, record);
+        instrumentation._callHook(span, record);
+
+        return original.apply(this, args);
+      };
+    };
+  }
+}
+
+function injectRecord(
+  spanContext: SpanContext,
+  record?: Record<string, string>
+) {
+  const fields = {
+    trace_id: spanContext.traceId,
+    span_id: spanContext.spanId,
+    trace_flags: `0${spanContext.traceFlags.toString(16)}`,
+  };
+
+  if (!record) {
+    return fields;
+  }
+
+  return Object.assign(record, fields);
+}
diff --git a/plugins/node/opentelemetry-instrumentation-winston/test/winston.test.ts b/plugins/node/opentelemetry-instrumentation-winston/test/winston.test.ts
new file mode 100644
index 0000000000..7b558f493c
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/test/winston.test.ts
@@ -0,0 +1,188 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.
+ */
+
+import {
+  InMemorySpanExporter,
+  SimpleSpanProcessor,
+} from '@opentelemetry/tracing';
+import {
+  context,
+  getSpan,
+  setSpan,
+  NoopTracerProvider,
+  Span,
+} from '@opentelemetry/api';
+import { NodeTracerProvider } from '@opentelemetry/node';
+import { isWrapped } from '@opentelemetry/instrumentation';
+import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
+import * as assert from 'assert';
+import * as sinon from 'sinon';
+import { Writable } from 'stream';
+import type * as Winston from 'winston';
+import type { Winston3Logger } from '../src/types';
+import { WinstonInstrumentation } from '../src/winston';
+
+const memoryExporter = new InMemorySpanExporter();
+const provider = new NodeTracerProvider();
+const tracer = provider.getTracer('default');
+provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
+context.setGlobalContextManager(new AsyncHooksContextManager());
+
+const kMessage = 'log-message';
+
+describe('WinstonInstrumentation', () => {
+  let logger: Winston3Logger;
+  let stream;
+  let writeSpy: sinon.SinonSpy;
+  let winston: typeof Winston;
+  let instrumentation: WinstonInstrumentation;
+
+  function testInjection(span: Span) {
+    logger.info(kMessage);
+    sinon.assert.calledOnce(writeSpy);
+    const { traceId, spanId, traceFlags } = span.context();
+    const record = JSON.parse(writeSpy.firstCall.args[0].toString());
+    assert.strictEqual(record['trace_id'], traceId);
+    assert.strictEqual(record['span_id'], spanId);
+    assert.strictEqual(record['trace_flags'], `0${traceFlags.toString(16)}`);
+    assert.strictEqual(kMessage, record['message']);
+    return record;
+  }
+
+  function testNoInjection() {
+    logger.info(kMessage);
+    sinon.assert.calledOnce(writeSpy);
+    const record = JSON.parse(writeSpy.firstCall.args[0].toString());
+    assert.strictEqual(record['trace_id'], undefined);
+    assert.strictEqual(record['span_id'], undefined);
+    assert.strictEqual(record['trace_flags'], undefined);
+    assert.strictEqual(kMessage, record['message']);
+    return record;
+  }
+
+  before(() => {
+    instrumentation = new WinstonInstrumentation();
+    instrumentation.enable();
+    winston = require('winston');
+    assert.ok(isWrapped(winston.createLogger()['write']));
+  });
+
+  describe('enabled instrumentation', () => {
+    beforeEach(() => {
+      stream = new Writable();
+      stream._write = () => {};
+      writeSpy = sinon.spy(stream, 'write');
+      logger = winston.createLogger({
+        transports: [
+          new winston.transports.Stream({
+            stream,
+          }),
+        ],
+      });
+    });
+
+    it('injects span context to records', () => {
+      const span = tracer.startSpan('abc');
+      context.with(setSpan(context.active(), span), () => {
+        testInjection(span);
+      });
+    });
+
+    it('calls the users log hook', () => {
+      const span = tracer.startSpan('abc');
+      instrumentation.setConfig({
+        enabled: true,
+        logHook: (_span, record) => {
+          record['resource.service.name'] = 'test-service';
+        },
+      });
+      context.with(setSpan(context.active(), span), () => {
+        const record = testInjection(span);
+        assert.strictEqual(record['resource.service.name'], 'test-service');
+      });
+    });
+
+    it('does not inject span context if no span is active', () => {
+      assert.strictEqual(getSpan(context.active()), undefined);
+      testNoInjection();
+    });
+
+    it('does not inject span context if span context is invalid', () => {
+      const noopTracer = new NoopTracerProvider().getTracer('noop');
+      const span = noopTracer.startSpan('noop');
+      context.with(setSpan(context.active(), span), () => {
+        testNoInjection();
+      });
+    });
+
+    it('does not propagate exceptions from user hooks', () => {
+      const span = tracer.startSpan('abc');
+      instrumentation.setConfig({
+        enabled: true,
+        logHook: () => {
+          throw new Error('Oops');
+        },
+      });
+      context.with(setSpan(context.active(), span), () => {
+        testInjection(span);
+      });
+    });
+  });
+
+  describe('disabled instrumentation', () => {
+    before(() => {
+      instrumentation.disable();
+    });
+
+    after(() => {
+      instrumentation.enable();
+    });
+
+    beforeEach(() => {
+      stream = new Writable();
+      stream._write = () => {};
+      writeSpy = sinon.spy(stream, 'write');
+      logger = winston.createLogger({
+        transports: [
+          new winston.transports.Stream({
+            stream,
+          }),
+        ],
+      });
+    });
+
+    it('does not inject span context', () => {
+      const span = tracer.startSpan('abc');
+      context.with(setSpan(context.active(), span), () => {
+        testNoInjection();
+      });
+    });
+
+    it('does not call log hook', () => {
+      const span = tracer.startSpan('abc');
+      instrumentation.setConfig({
+        enabled: false,
+        logHook: (_span, record) => {
+          record['resource.service.name'] = 'test-service';
+        },
+      });
+      context.with(setSpan(context.active(), span), () => {
+        const record = testNoInjection();
+        assert.strictEqual(record['resource.service.name'], undefined);
+      });
+    });
+  });
+});
diff --git a/plugins/node/opentelemetry-instrumentation-winston/tsconfig.json b/plugins/node/opentelemetry-instrumentation-winston/tsconfig.json
new file mode 100644
index 0000000000..28be80d266
--- /dev/null
+++ b/plugins/node/opentelemetry-instrumentation-winston/tsconfig.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../../../tsconfig.base",
+  "compilerOptions": {
+    "rootDir": ".",
+    "outDir": "build"
+  },
+  "include": [
+    "src/**/*.ts",
+    "test/**/*.ts"
+  ]
+}