Skip to content

Commit 196ec20

Browse files
committed
Fix: avoid gsub (frame dependent) usage from Java
`RubyString#gsub` requires a (Ruby) frame to be present. The method attempts to set a backref for the current caller's frame. When the frame stack is empty there isn't really a place to set $~. This can happen when a LogStash::Util::Loggable#logger is retrieved, from the input worker thread while not being nested in any block. Fixes #11874
1 parent 6a727ca commit 196ec20

File tree

3 files changed

+56
-38
lines changed

3 files changed

+56
-38
lines changed

logstash-core/src/main/java/org/logstash/log/DeprecationLoggerExt.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,21 @@ public DeprecationLoggerExt(final Ruby runtime, final RubyClass metaClass) {
4040
super(runtime, metaClass);
4141
}
4242

43+
DeprecationLoggerExt(final Ruby runtime, final RubyClass metaClass, final String loggerName) {
44+
super(runtime, metaClass);
45+
initialize(loggerName);
46+
}
47+
4348
@JRubyMethod
4449
public DeprecationLoggerExt initialize(final ThreadContext context, final IRubyObject loggerName) {
45-
logger = new DefaultDeprecationLogger(loggerName.asJavaString());
50+
initialize(loggerName.asJavaString());
4651
return this;
4752
}
4853

54+
private void initialize(final String loggerName) {
55+
logger = new DefaultDeprecationLogger(loggerName);
56+
}
57+
4958
@JRubyMethod(name = "deprecated", required = 1, optional = 1)
5059
public IRubyObject rubyDeprecated(final ThreadContext context, final IRubyObject[] args) {
5160
if (args.length > 1) {

logstash-core/src/main/java/org/logstash/log/LoggableExt.java

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@
2222

2323
import org.jruby.RubyClass;
2424
import org.jruby.RubyModule;
25-
import org.jruby.RubyString;
2625
import org.jruby.anno.JRubyMethod;
2726
import org.jruby.anno.JRubyModule;
28-
import org.jruby.runtime.Block;
2927
import org.jruby.runtime.ThreadContext;
3028
import org.jruby.runtime.builtin.IRubyObject;
3129
import org.jruby.runtime.builtin.InstanceVariables;
3230
import org.logstash.RubyUtil;
3331

34-
import static org.logstash.RubyUtil.RUBY;
32+
import java.util.Locale;
33+
34+
import static org.logstash.log.SlowLoggerExt.toLong;
3535

3636
@JRubyModule(name = "Loggable")
3737
public final class LoggableExt {
@@ -64,24 +64,18 @@ public static IRubyObject deprecationLogger(final ThreadContext context, final I
6464
return self.getSingletonClass().callMethod(context, "deprecation_logger");
6565
}
6666

67-
private static RubyString log4jName(final ThreadContext context, final RubyModule self) {
68-
IRubyObject name = self.name(context);
69-
if (name.isNil()) {
70-
final RubyClass clazz;
67+
private static String log4jName(final RubyModule self) {
68+
String name;
69+
if (self.getBaseName() == null) { // anonymous module/class
70+
RubyModule real = self;
7171
if (self instanceof RubyClass) {
72-
clazz = ((RubyClass) self).getRealClass();
73-
} else {
74-
clazz = self.getMetaClass();
75-
}
76-
name = clazz.name(context);
77-
if (name.isNil()) {
78-
name = clazz.to_s();
72+
real = ((RubyClass) self).getRealClass();
7973
}
74+
name = real.getName(); // for anonymous: "#<Class:0xcafebabe>"
75+
} else {
76+
name = self.getName();
8077
}
81-
return ((RubyString) ((RubyString) name).gsub(
82-
context, RUBY.newString("::"), RUBY.newString("."),
83-
Block.NULL_BLOCK
84-
)).downcase(context);
78+
return name.replace("::", ".").toLowerCase(Locale.ENGLISH);
8579
}
8680

8781
/**
@@ -105,9 +99,8 @@ public static IRubyObject logger(final ThreadContext context, final IRubyObject
10599
}
106100
IRubyObject logger = instanceVariables.getInstanceVariable("logger");
107101
if (logger == null || logger.isNil()) {
108-
logger = RubyUtil.LOGGER.callMethod(context, "new",
109-
LoggableExt.log4jName(context, (RubyModule) self)
110-
);
102+
final String loggerName = log4jName((RubyModule) self);
103+
logger = RubyUtil.LOGGER.callMethod(context, "new", context.runtime.newString(loggerName));
111104
instanceVariables.setInstanceVariable("logger", logger);
112105
}
113106
return logger;
@@ -117,18 +110,15 @@ public static IRubyObject logger(final ThreadContext context, final IRubyObject
117110
public static SlowLoggerExt slowLogger(final ThreadContext context,
118111
final IRubyObject self, final IRubyObject[] args) {
119112
final InstanceVariables instanceVariables = self.getInstanceVariables();
120-
SlowLoggerExt logger =
121-
(SlowLoggerExt) instanceVariables.getInstanceVariable("slow_logger");
113+
IRubyObject logger = instanceVariables.getInstanceVariable("slow_logger");
122114
if (logger == null || logger.isNil()) {
123-
logger = new SlowLoggerExt(context.runtime, RubyUtil.SLOW_LOGGER).initialize(
124-
context, new IRubyObject[]{
125-
LoggableExt.log4jName(context, (RubyModule) self), args[0], args[1],
126-
args[2], args[3]
127-
}
115+
final String loggerName = log4jName((RubyModule) self);
116+
logger = new SlowLoggerExt(context.runtime, RubyUtil.SLOW_LOGGER, loggerName,
117+
toLong(args[0]), toLong(args[1]), toLong(args[2]), toLong(args[3])
128118
);
129119
instanceVariables.setInstanceVariable("slow_logger", logger);
130120
}
131-
return logger;
121+
return (SlowLoggerExt) logger;
132122
}
133123

134124
@JRubyMethod(name = "deprecation_logger", meta = true)
@@ -141,8 +131,8 @@ public static IRubyObject deprecationLogger(final ThreadContext context, final I
141131
}
142132
IRubyObject logger = instanceVariables.getInstanceVariable("deprecation_logger");
143133
if (logger == null || logger.isNil()) {
144-
logger = new DeprecationLoggerExt(context.runtime, RubyUtil.DEPRECATION_LOGGER)
145-
.initialize(context, LoggableExt.log4jName(context, (RubyModule) self));
134+
final String loggerName = log4jName((RubyModule) self);
135+
logger = new DeprecationLoggerExt(context.runtime, RubyUtil.DEPRECATION_LOGGER, loggerName);
146136
instanceVariables.setInstanceVariable("deprecation_logger", logger);
147137
}
148138
return logger;

logstash-core/src/main/java/org/logstash/log/SlowLoggerExt.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,36 @@ public SlowLoggerExt(final Ruby runtime, final RubyClass metaClass) {
5555
super(runtime, metaClass);
5656
}
5757

58+
SlowLoggerExt(final Ruby runtime, final RubyClass metaClass, final String loggerName,
59+
final long warnThreshold, final long infoThreshold,
60+
final long debugThreshold, final long traceThreshold) {
61+
super(runtime, metaClass);
62+
initialize(loggerName, warnThreshold, infoThreshold, debugThreshold, traceThreshold);
63+
}
64+
5865
@JRubyMethod(required = 5)
5966
public SlowLoggerExt initialize(final ThreadContext context, final IRubyObject[] args) {
60-
String loggerName = args[0].asJavaString();
61-
slowLogger = LogManager.getLogger("slowlog." + loggerName);
62-
warnThreshold = ((RubyNumeric) args[1]).getLongValue();
63-
infoThreshold = ((RubyNumeric) args[2]).getLongValue();
64-
debugThreshold = ((RubyNumeric) args[3]).getLongValue();
65-
traceThreshold = ((RubyNumeric) args[4]).getLongValue();
67+
initialize(args[0].asJavaString(), toLong(args[1]), toLong(args[2]), toLong(args[3]), toLong(args[4]));
6668
return this;
6769
}
6870

71+
private void initialize(final String loggerName,
72+
final long warnThreshold, final long infoThreshold,
73+
final long debugThreshold, final long traceThreshold) {
74+
slowLogger = LogManager.getLogger("slowlog." + loggerName);
75+
this.warnThreshold = warnThreshold;
76+
this.infoThreshold = infoThreshold;
77+
this.debugThreshold = debugThreshold;
78+
this.traceThreshold = traceThreshold;
79+
}
80+
81+
static long toLong(final IRubyObject value) {
82+
if (!(value instanceof RubyNumeric)) {
83+
throw RubyUtil.RUBY.newTypeError("Numeric expected, got " + value.getMetaClass());
84+
}
85+
return ((RubyNumeric) value).getLongValue();
86+
}
87+
6988
private RubyHash asData(final ThreadContext context, final IRubyObject pluginParams,
7089
final IRubyObject event, final IRubyObject durationNanos) {
7190
RubyHash data = RubyHash.newHash(context.runtime);

0 commit comments

Comments
 (0)